@aspiresys/visor 1.2.11 → 1.3.1
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 +15 -52
- package/dist/index.js +16 -35
- package/dist/matcher.d.ts +3 -0
- package/dist/matcher.js +86 -14
- package/dist/mouse.d.ts +3 -9
- package/dist/mouse.js +3 -2
- package/dist/ocr.d.ts +2 -6
- package/dist/region.d.ts +9 -0
- package/dist/region.js +15 -0
- package/dist/text.d.ts +2 -12
- package/dist/text.js +2 -7
- package/dist/types.d.ts +165 -1
- package/dist/types.js +268 -0
- package/inspector/index.html +10 -4
- package/inspector/main.js +11 -6
- package/inspector/src/renderer.js +76 -72
- package/inspector/start.js +1 -4
- package/inspector/styles.css +44 -31
- package/inspector/templates/div.png +0 -0
- package/inspector/templates/match.png +0 -0
- package/inspector/templates/template.png +0 -0
- package/package.json +7 -1
- package/readme.md +182 -37
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;
|
package/inspector/index.html
CHANGED
|
@@ -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,10 +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
|
-
|
|
27
|
-
|
|
28
|
-
|
|
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>
|
|
29
35
|
</div>
|
|
30
36
|
</div>
|
|
31
37
|
</div>
|
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
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
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();
|
|
@@ -31,6 +31,7 @@ captureBtn.addEventListener("click",
|
|
|
31
31
|
canvas.width = image.width * scale;
|
|
32
32
|
canvas.height = image.height * scale;
|
|
33
33
|
matchResult.textContent = " No Match Tested ";
|
|
34
|
+
ctx.imageSmoothingEnabled = false;
|
|
34
35
|
ctx.drawImage(
|
|
35
36
|
image,
|
|
36
37
|
0,
|
|
@@ -89,24 +90,14 @@ canvas.addEventListener("mousemove",
|
|
|
89
90
|
canvas.addEventListener("mouseup",
|
|
90
91
|
() => {
|
|
91
92
|
selecting = false;
|
|
92
|
-
console.log(
|
|
93
|
-
startX,
|
|
94
|
-
startY,
|
|
95
|
-
endX,
|
|
96
|
-
endY
|
|
97
|
-
);
|
|
93
|
+
console.log(startX, startY, endX, endY);
|
|
98
94
|
}
|
|
99
95
|
);
|
|
100
96
|
|
|
101
97
|
|
|
102
98
|
saveBtn.addEventListener("click",
|
|
103
99
|
async () => {
|
|
104
|
-
if (
|
|
105
|
-
startX === undefined ||
|
|
106
|
-
startY === undefined ||
|
|
107
|
-
endX === undefined ||
|
|
108
|
-
endY === undefined
|
|
109
|
-
) {
|
|
100
|
+
if (startX === undefined || startY === undefined || endX === undefined || endY === undefined) {
|
|
110
101
|
alert("Please select an area first.");
|
|
111
102
|
return;
|
|
112
103
|
}
|
|
@@ -120,17 +111,17 @@ saveBtn.addEventListener("click",
|
|
|
120
111
|
return;
|
|
121
112
|
}
|
|
122
113
|
await window.visor.saveTemplate({
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
114
|
+
x: Math.min(startX, endX),
|
|
115
|
+
y: Math.min(startY, endY),
|
|
116
|
+
width: Math.abs(endX - startX),
|
|
117
|
+
height: Math.abs(endY - startY),
|
|
118
|
+
outputPath: filePath
|
|
119
|
+
});
|
|
129
120
|
const fileName = filePath.split(/[\\/]/).pop();
|
|
130
121
|
selectedTemplatePath = filePath;
|
|
131
122
|
currentTemplate.textContent = fileName;
|
|
132
123
|
currentTemplate.title = filePath;
|
|
133
|
-
matchResult.textContent = "
|
|
124
|
+
matchResult.textContent = "<b>No Match Tested</b>";
|
|
134
125
|
ctx.drawImage(
|
|
135
126
|
image,
|
|
136
127
|
0,
|
|
@@ -143,29 +134,36 @@ saveBtn.addEventListener("click",
|
|
|
143
134
|
}
|
|
144
135
|
);
|
|
145
136
|
|
|
146
|
-
testMatchBtn.addEventListener("click",
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
result
|
|
165
|
-
)
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
137
|
+
testMatchBtn.addEventListener("click",
|
|
138
|
+
async () => {
|
|
139
|
+
if (!selectedTemplatePath) {
|
|
140
|
+
alert("Please load or save a template first.");
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
const confidence = parseFloat(confidenceInput.value);
|
|
144
|
+
if (isNaN(confidence) || confidence < 0 || confidence > 1) {
|
|
145
|
+
alert("Confidence must be between 0 and 1");
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
matchResult.textContent = "🔍 Matching...";
|
|
149
|
+
await new Promise(resolve => setTimeout(resolve, 10));
|
|
150
|
+
const result = await window.visor.testMatch({
|
|
151
|
+
templatePath: selectedTemplatePath,
|
|
152
|
+
confidence: confidence,
|
|
153
|
+
//isMultiMatch: document.getElementById("multiScaleCheck").checked
|
|
154
|
+
});
|
|
155
|
+
console.log("Match Result:", result);
|
|
156
|
+
if (!result || result.length==0) {
|
|
157
|
+
matchResult.textContent = "✗ MATCH NOT FOUND";
|
|
158
|
+
ctx.drawImage(
|
|
159
|
+
image,
|
|
160
|
+
0,
|
|
161
|
+
0,
|
|
162
|
+
canvas.width,
|
|
163
|
+
canvas.height
|
|
164
|
+
);
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
169
167
|
ctx.drawImage(
|
|
170
168
|
image,
|
|
171
169
|
0,
|
|
@@ -173,37 +171,43 @@ testMatchBtn.addEventListener("click", async () => {
|
|
|
173
171
|
canvas.width,
|
|
174
172
|
canvas.height
|
|
175
173
|
);
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
Location:
|
|
200
|
-
(${result.x}, ${result.y})
|
|
201
|
-
|
|
202
|
-
Size:
|
|
203
|
-
${result.width} x ${result.height}
|
|
204
|
-
`;
|
|
174
|
+
let matchR = "<b>✓ MATCH FOUND</b>\n";
|
|
175
|
+
let i = 1;
|
|
176
|
+
for(const match of result){
|
|
177
|
+
ctx.strokeStyle = "lime";
|
|
178
|
+
ctx.lineWidth = 2;
|
|
179
|
+
ctx.strokeRect(
|
|
180
|
+
match.x / imageScaleX,
|
|
181
|
+
match.y / imageScaleY,
|
|
182
|
+
match.width / imageScaleX,
|
|
183
|
+
match.height / imageScaleY
|
|
184
|
+
);
|
|
185
|
+
matchR += `<b>Match:</b> ${i}
|
|
186
|
+
<b>Confidence:</b>
|
|
187
|
+
${match.confidence.toFixed(3)}
|
|
188
|
+
<b>Location:</b>
|
|
189
|
+
(${match.x}, ${match.y})
|
|
190
|
+
<b>Size:</b>
|
|
191
|
+
${match.width} x ${match.height}
|
|
192
|
+
<hr>
|
|
193
|
+
`;
|
|
194
|
+
i += 1;
|
|
195
|
+
}
|
|
196
|
+
matchResult.innerHTML = matchR;
|
|
205
197
|
});
|
|
206
198
|
|
|
199
|
+
templatePreview.addEventListener("click",
|
|
200
|
+
() => {
|
|
201
|
+
modalImage.src = templatePreview.src;
|
|
202
|
+
imageModal.style.display = "flex";
|
|
203
|
+
}
|
|
204
|
+
);
|
|
205
|
+
|
|
206
|
+
imageModal.addEventListener("click",
|
|
207
|
+
() => {
|
|
208
|
+
imageModal.style.display = "none";
|
|
209
|
+
}
|
|
210
|
+
);
|
|
207
211
|
|
|
208
212
|
loadTemplateBtn.addEventListener("click",
|
|
209
213
|
async () => {
|
|
@@ -215,7 +219,7 @@ loadTemplateBtn.addEventListener("click",
|
|
|
215
219
|
selectedTemplatePath = path;
|
|
216
220
|
currentTemplate.textContent = fileName;
|
|
217
221
|
currentTemplate.title = path;
|
|
218
|
-
matchResult.
|
|
222
|
+
matchResult.innerHTML = "<b>No Match Tested</b>";
|
|
219
223
|
ctx.drawImage(
|
|
220
224
|
image,
|
|
221
225
|
0,
|
package/inspector/start.js
CHANGED
package/inspector/styles.css
CHANGED
|
@@ -199,18 +199,29 @@ body {
|
|
|
199
199
|
gap: 10px;
|
|
200
200
|
}
|
|
201
201
|
|
|
202
|
-
input[type="
|
|
202
|
+
input[type="range"] {
|
|
203
203
|
background: rgba(37, 37, 38, 0.8);
|
|
204
204
|
color: #00d4ff;
|
|
205
|
-
|
|
206
|
-
padding: 5px 6px;
|
|
207
|
-
border-radius: 6px;
|
|
208
|
-
font-size: 0.8em;
|
|
209
|
-
width: 60px;
|
|
205
|
+
width: 100px;
|
|
210
206
|
transition: all 0.3s ease;
|
|
207
|
+
height: 5px;
|
|
211
208
|
}
|
|
212
209
|
|
|
213
|
-
input[type="number"]
|
|
210
|
+
input[type="number"] {
|
|
211
|
+
color: rgb(0, 212, 255);
|
|
212
|
+
font-size: 0.8em;
|
|
213
|
+
width: 60px;
|
|
214
|
+
background: rgba(37, 37, 38, 0.8);
|
|
215
|
+
border-width: 1px;
|
|
216
|
+
border-style: solid;
|
|
217
|
+
border-color: rgba(0, 212, 255, 0.3);
|
|
218
|
+
border-image: initial;
|
|
219
|
+
padding: 5px 6px;
|
|
220
|
+
border-radius: 6px;
|
|
221
|
+
transition: all 0.3s ease;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
input[type="range"]:focus, input[type="number"]:focus {
|
|
214
225
|
outline: none;
|
|
215
226
|
border-color: #00d4ff;
|
|
216
227
|
box-shadow: 0 0 12px rgba(0, 212, 255, 0.2);
|
|
@@ -286,6 +297,7 @@ input[type="number"]:focus {
|
|
|
286
297
|
display: none;
|
|
287
298
|
margin-top: 15px;
|
|
288
299
|
margin-bottom: 15px;
|
|
300
|
+
cursor: zoom-in;
|
|
289
301
|
}
|
|
290
302
|
|
|
291
303
|
.template-preview[src]:not([src=""]) {
|
|
@@ -344,22 +356,23 @@ input[type="number"]:focus {
|
|
|
344
356
|
}
|
|
345
357
|
|
|
346
358
|
#matchResult {
|
|
347
|
-
|
|
348
|
-
|
|
359
|
+
padding: 12px;
|
|
360
|
+
min-height: 157px;
|
|
361
|
+
max-height: 300px;
|
|
362
|
+
overflow-y: auto;
|
|
363
|
+
max-width: max-content;
|
|
364
|
+
background: rgba(255, 255, 255, 0.03);
|
|
365
|
+
border: 1px solid rgba(0, 212, 255, 0.2);
|
|
349
366
|
border-radius: 8px;
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
min-height: 60px;
|
|
353
|
-
display: inline-flex;
|
|
354
|
-
align-items: center;
|
|
355
|
-
justify-content: center;
|
|
356
|
-
color: #888;
|
|
367
|
+
font-family: Consolas, monospace;
|
|
368
|
+
white-space: pre-line;
|
|
357
369
|
font-size: 0.8em;
|
|
370
|
+
border-color: rgb(76, 175, 79);
|
|
358
371
|
}
|
|
359
372
|
|
|
360
373
|
#matchResult:not(:empty) {
|
|
361
374
|
background: rgba(76, 175, 80, 0.1);
|
|
362
|
-
border-color:
|
|
375
|
+
border-color: rgb(76, 175, 79);
|
|
363
376
|
color: #e0e0e0;
|
|
364
377
|
}
|
|
365
378
|
|
|
@@ -424,22 +437,22 @@ input[type="number"]:focus {
|
|
|
424
437
|
|
|
425
438
|
.result-panel {
|
|
426
439
|
width: 250px;
|
|
427
|
-
|
|
428
440
|
flex-shrink: 0;
|
|
429
441
|
}
|
|
430
442
|
|
|
431
|
-
#matchResult {
|
|
432
|
-
padding: 12px;
|
|
433
|
-
|
|
434
|
-
min-height: 157px;
|
|
435
|
-
|
|
436
|
-
background: rgba(255, 255, 255, 0.03);
|
|
437
|
-
|
|
438
|
-
border: 1px solid rgba(0, 212, 255, 0.2);
|
|
439
|
-
|
|
440
|
-
border-radius: 8px;
|
|
441
|
-
|
|
442
|
-
font-family: Consolas, monospace;
|
|
443
443
|
|
|
444
|
-
|
|
444
|
+
.image-modal {
|
|
445
|
+
display: none;
|
|
446
|
+
position: fixed;
|
|
447
|
+
inset: 0;
|
|
448
|
+
background:
|
|
449
|
+
rgba(0,0,0,0.8);
|
|
450
|
+
justify-content: center;
|
|
451
|
+
align-items: center;
|
|
452
|
+
z-index: 9999;
|
|
445
453
|
}
|
|
454
|
+
|
|
455
|
+
.image-modal img {
|
|
456
|
+
max-width: 90vw;
|
|
457
|
+
max-height: 90vh;
|
|
458
|
+
}
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/package.json
CHANGED
|
@@ -1,12 +1,18 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aspiresys/visor",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.1",
|
|
4
4
|
"main": "dist/index.js",
|
|
5
5
|
"types": "dist/index.d.ts",
|
|
6
6
|
"scripts": {
|
|
7
7
|
"build": "tsc",
|
|
8
8
|
"start": "ts-node src/test.ts"
|
|
9
9
|
},
|
|
10
|
+
"exports": {
|
|
11
|
+
".": {
|
|
12
|
+
"types": "./dist/index.d.ts",
|
|
13
|
+
"default": "./dist/index.js"
|
|
14
|
+
}
|
|
15
|
+
},
|
|
10
16
|
"dependencies": {
|
|
11
17
|
"@nut-tree-fork/nut-js": "^4.1.0",
|
|
12
18
|
"@techstark/opencv-js": "^4.12.0-release.1",
|