ucu-mcp 0.4.0 → 0.4.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/CHANGELOG.md +50 -4
- package/dist/bin/ucu-mcp.js +1 -1
- package/dist/src/index.d.ts +2 -2
- package/dist/src/index.js +2 -2
- package/dist/src/mcp/server.js +1 -1
- package/dist/src/mcp/tools/app-tools.d.ts +2 -0
- package/dist/src/mcp/tools/app-tools.js +220 -0
- package/dist/src/mcp/tools/element-tools.d.ts +23 -0
- package/dist/src/mcp/tools/element-tools.js +59 -0
- package/dist/src/mcp/tools/helpers.d.ts +82 -0
- package/dist/src/mcp/tools/helpers.js +243 -0
- package/dist/src/mcp/tools/index.d.ts +19 -0
- package/dist/src/mcp/tools/index.js +54 -0
- package/dist/src/mcp/tools/input-tools.d.ts +2 -0
- package/dist/src/mcp/tools/input-tools.js +66 -0
- package/dist/src/mcp/tools/keyboard-tools.d.ts +2 -0
- package/dist/src/mcp/tools/keyboard-tools.js +35 -0
- package/dist/src/mcp/tools/screen-tools.d.ts +2 -0
- package/dist/src/mcp/tools/screen-tools.js +69 -0
- package/dist/src/mcp/tools.d.ts +9 -0
- package/dist/src/mcp/tools.js +87 -23
- package/dist/src/platform/base.d.ts +3 -0
- package/dist/src/platform/jxa-helpers.d.ts +11 -0
- package/dist/src/platform/jxa-helpers.js +206 -0
- package/dist/src/platform/macos/ax-tree.d.ts +4 -0
- package/dist/src/platform/macos/ax-tree.js +462 -0
- package/dist/src/platform/macos/base.d.ts +57 -0
- package/dist/src/platform/macos/base.js +92 -0
- package/dist/src/platform/macos/clipboard.d.ts +3 -0
- package/dist/src/platform/macos/clipboard.js +20 -0
- package/dist/src/platform/macos/element.d.ts +4 -0
- package/dist/src/platform/macos/element.js +212 -0
- package/dist/src/platform/macos/focus.d.ts +3 -0
- package/dist/src/platform/macos/focus.js +33 -0
- package/dist/src/platform/macos/helpers.d.ts +35 -0
- package/dist/src/platform/macos/helpers.js +54 -0
- package/dist/src/platform/macos/index.d.ts +2 -0
- package/dist/src/platform/macos/index.js +1 -0
- package/dist/src/platform/macos/input.d.ts +9 -0
- package/dist/src/platform/macos/input.js +62 -0
- package/dist/src/platform/macos/screen.d.ts +7 -0
- package/dist/src/platform/macos/screen.js +197 -0
- package/dist/src/platform/macos/window.d.ts +6 -0
- package/dist/src/platform/macos/window.js +251 -0
- package/dist/src/platform/macos.js +71 -563
- package/dist/src/util/errors.d.ts +7 -2
- package/dist/src/util/errors.js +7 -3
- package/native/cgevent/cgevent-helper +0 -0
- package/native/ocr/ocr-helper +0 -0
- package/native/windowlist/windowlist-helper +0 -0
- package/package.json +1 -1
|
@@ -4,9 +4,11 @@ import { promisify } from "node:util";
|
|
|
4
4
|
import { captureFullScreen, captureRegion } from "../utils/screenshot.js";
|
|
5
5
|
import { click as inputClick, doubleClick as inputDoubleClick, move as inputMove, drag as inputDrag, scroll as inputScroll, typeText, pressShortcut } from "../utils/input.js";
|
|
6
6
|
import { CaptureError, ElementNotFoundError, InputSynthesisError, PermissionError, PlatformError, TargetStaleError, UcuError, WindowNotFoundError } from "../util/errors.js";
|
|
7
|
+
import { logger } from "../util/logger.js";
|
|
7
8
|
import { existsSync } from "node:fs";
|
|
8
9
|
import { join, dirname } from "node:path";
|
|
9
10
|
import { fileURLToPath } from "node:url";
|
|
11
|
+
import { jxaChildElements, jxaGetBounds, jxaIsVisible, jxaElementActionHelpers } from "./jxa-helpers.js";
|
|
10
12
|
const __macosDirname = dirname(fileURLToPath(import.meta.url));
|
|
11
13
|
const execFileAsync = promisify(execFile);
|
|
12
14
|
function errorMessage(error) {
|
|
@@ -149,8 +151,9 @@ export class MacOSPlatform {
|
|
|
149
151
|
return;
|
|
150
152
|
try {
|
|
151
153
|
const { appName } = this.savedFocus;
|
|
154
|
+
const appNameLiteral = JSON.stringify(appName);
|
|
152
155
|
execFileSync("osascript", [
|
|
153
|
-
"-e", `tell application
|
|
156
|
+
"-e", `tell application ${appNameLiteral} to activate`,
|
|
154
157
|
], { timeout: 5000 });
|
|
155
158
|
}
|
|
156
159
|
catch {
|
|
@@ -195,8 +198,9 @@ export class MacOSPlatform {
|
|
|
195
198
|
], { encoding: "utf-8", timeout: 5000 }).trim();
|
|
196
199
|
return JSON.parse(out);
|
|
197
200
|
}
|
|
198
|
-
catch {
|
|
199
|
-
|
|
201
|
+
catch (error) {
|
|
202
|
+
logger.warn("getScreenSize failed, using fallback", { error: errorMessage(error) });
|
|
203
|
+
return { width: 1920, height: 1080, scaleFactor: 2, estimated: true };
|
|
200
204
|
}
|
|
201
205
|
}
|
|
202
206
|
isScreenLocked() {
|
|
@@ -208,7 +212,8 @@ export class MacOSPlatform {
|
|
|
208
212
|
return /"IOConsoleLocked"\s*=\s*Yes/.test(out);
|
|
209
213
|
}
|
|
210
214
|
catch {
|
|
211
|
-
|
|
215
|
+
// Fail-closed: if we can't determine lock state, assume locked
|
|
216
|
+
return true;
|
|
212
217
|
}
|
|
213
218
|
}
|
|
214
219
|
// ── Window Management ───────────────────────────────────────────────────
|
|
@@ -235,14 +240,19 @@ export class MacOSPlatform {
|
|
|
235
240
|
}
|
|
236
241
|
JSON.stringify(result);
|
|
237
242
|
`;
|
|
238
|
-
|
|
239
|
-
"
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
+
try {
|
|
244
|
+
const out = execFileSync("osascript", [
|
|
245
|
+
"-l", "JavaScript",
|
|
246
|
+
"-e", jxaScript,
|
|
247
|
+
], { encoding: "utf-8", timeout: 10000 }).trim();
|
|
248
|
+
return JSON.parse(out);
|
|
249
|
+
}
|
|
250
|
+
catch (error) {
|
|
251
|
+
rethrowAccessibilityError(error, "list_apps");
|
|
252
|
+
}
|
|
243
253
|
}
|
|
244
254
|
async focusApp(app) {
|
|
245
|
-
const
|
|
255
|
+
const appLiteral = JSON.stringify(app);
|
|
246
256
|
this.windowCache = undefined;
|
|
247
257
|
// NOTE: We intentionally do NOT call AppleScript "activate" here.
|
|
248
258
|
// focus_app sets the internal target context so subsequent operations
|
|
@@ -269,16 +279,14 @@ export class MacOSPlatform {
|
|
|
269
279
|
// bare WindowNotFoundError("CC Switch") was indistinguishable from
|
|
270
280
|
// "the app is not running", which led models to retry forever.
|
|
271
281
|
this.activeTarget = undefined; // Clear stale target on focus failure
|
|
272
|
-
const err = new WindowNotFoundError(app
|
|
273
|
-
err.hint =
|
|
274
|
-
"list_windows returned no match for this app. If the app is running, " +
|
|
282
|
+
const err = new WindowNotFoundError(app, { hint: "list_windows returned no match for this app. If the app is running, " +
|
|
275
283
|
"the most likely cause is that it is an Electron app whose AX tree is " +
|
|
276
284
|
"not exposed to System Events (System Settings > Privacy & Security > " +
|
|
277
285
|
"Accessibility must be granted to the Electron process itself, not just " +
|
|
278
286
|
"to the host terminal). Pixel-level workaround: call screenshot to " +
|
|
279
287
|
"capture the screen, then ocr to locate UI text and get its bounding " +
|
|
280
288
|
"box coordinates, then click(x, y) at those screen coordinates. " +
|
|
281
|
-
"Alternatively, modify the app's config file or database directly.";
|
|
289
|
+
"Alternatively, modify the app's config file or database directly." });
|
|
282
290
|
throw err;
|
|
283
291
|
}
|
|
284
292
|
this.activeTarget = {
|
|
@@ -308,10 +316,10 @@ export class MacOSPlatform {
|
|
|
308
316
|
].some((name) => normalized.includes(name));
|
|
309
317
|
if (!knownBrowser)
|
|
310
318
|
return undefined;
|
|
311
|
-
const
|
|
319
|
+
const appLiteral = JSON.stringify(appName);
|
|
312
320
|
const jxaScript = `
|
|
313
321
|
function run() {
|
|
314
|
-
var appName =
|
|
322
|
+
var appName = ${appLiteral};
|
|
315
323
|
try {
|
|
316
324
|
var app = Application(appName);
|
|
317
325
|
var url = "";
|
|
@@ -468,11 +476,16 @@ export class MacOSPlatform {
|
|
|
468
476
|
}
|
|
469
477
|
JSON.stringify(result);
|
|
470
478
|
`;
|
|
471
|
-
|
|
472
|
-
"
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
479
|
+
try {
|
|
480
|
+
const jxaOut = execFileSync("osascript", [
|
|
481
|
+
"-l", "JavaScript",
|
|
482
|
+
"-e", jxaScript
|
|
483
|
+
], { encoding: "utf-8", timeout: 15000 });
|
|
484
|
+
return JSON.parse(jxaOut.trim());
|
|
485
|
+
}
|
|
486
|
+
catch (error) {
|
|
487
|
+
rethrowAccessibilityError(error, "list_windows_jxa");
|
|
488
|
+
}
|
|
476
489
|
}
|
|
477
490
|
async getWindowState(windowId, depth, includeBounds = true) {
|
|
478
491
|
if (!windowId || windowId === this.activeTarget?.windowId) {
|
|
@@ -484,18 +497,14 @@ export class MacOSPlatform {
|
|
|
484
497
|
}
|
|
485
498
|
const maxDepth = Math.min(depth || 3, 10);
|
|
486
499
|
const maxElements = 50;
|
|
487
|
-
const
|
|
500
|
+
const windowIdLiteral = JSON.stringify(resolvedWindowId);
|
|
488
501
|
const targetWindow = (await this.listWindows(true)).find((w) => w.id === resolvedWindowId);
|
|
489
502
|
const targetJson = JSON.stringify(targetWindow ?? null);
|
|
490
503
|
try {
|
|
491
504
|
const jxaScript = `
|
|
492
505
|
ObjC.import('AppKit');
|
|
493
506
|
var se = Application('System Events');
|
|
494
|
-
|
|
495
|
-
try { return elem.uiElements(); } catch(e1) {
|
|
496
|
-
try { return elem.elements(); } catch(e2) { return []; }
|
|
497
|
-
}
|
|
498
|
-
}
|
|
507
|
+
${jxaChildElements()}
|
|
499
508
|
var result = {window: null, focusedElement: null, tree: null, error: null};
|
|
500
509
|
var target = ${targetJson};
|
|
501
510
|
var includeBounds = ${includeBounds ? "true" : "false"};
|
|
@@ -506,7 +515,7 @@ export class MacOSPlatform {
|
|
|
506
515
|
|
|
507
516
|
function windowMatches(win, proc) {
|
|
508
517
|
if (!target) {
|
|
509
|
-
try { return String(win.id()) === String(
|
|
518
|
+
try { return String(win.id()) === String(${windowIdLiteral}); } catch(e) { return false; }
|
|
510
519
|
}
|
|
511
520
|
try {
|
|
512
521
|
if (target.pid && proc.unixId && proc.unixId() !== target.pid) return false;
|
|
@@ -526,7 +535,7 @@ export class MacOSPlatform {
|
|
|
526
535
|
closeEnough(size[1], b.height, 24);
|
|
527
536
|
} catch(e) {}
|
|
528
537
|
|
|
529
|
-
try { return String(win.id()) === String(
|
|
538
|
+
try { return String(win.id()) === String(${windowIdLiteral}); } catch(e) {}
|
|
530
539
|
return false;
|
|
531
540
|
}
|
|
532
541
|
|
|
@@ -534,7 +543,7 @@ export class MacOSPlatform {
|
|
|
534
543
|
var foundProc = null;
|
|
535
544
|
|
|
536
545
|
// Fast path: resolve "ProcessName/winN" format directly
|
|
537
|
-
var idParts =
|
|
546
|
+
var idParts = ${windowIdLiteral}.split('/');
|
|
538
547
|
if (idParts.length >= 2 && idParts[0]) {
|
|
539
548
|
var procName = idParts[0];
|
|
540
549
|
var winIdx = 0;
|
|
@@ -575,7 +584,7 @@ export class MacOSPlatform {
|
|
|
575
584
|
var winPos = foundWin.position();
|
|
576
585
|
var winSize = foundWin.size();
|
|
577
586
|
result.window = {
|
|
578
|
-
id: String(
|
|
587
|
+
id: String(${windowIdLiteral}),
|
|
579
588
|
title: foundWin.name() || '',
|
|
580
589
|
processName: foundProc.name() || '',
|
|
581
590
|
pid: foundProc.unixId ? foundProc.unixId() : 0,
|
|
@@ -822,7 +831,7 @@ export class MacOSPlatform {
|
|
|
822
831
|
}
|
|
823
832
|
}
|
|
824
833
|
async ocrJxa(tmpPath, screenSize, scaleFactor, region, buf) {
|
|
825
|
-
const
|
|
834
|
+
const pathLiteral = JSON.stringify(tmpPath);
|
|
826
835
|
const jxaScript = `
|
|
827
836
|
function run() {
|
|
828
837
|
ObjC.import('Vision');
|
|
@@ -830,7 +839,7 @@ export class MacOSPlatform {
|
|
|
830
839
|
ObjC.import('Foundation');
|
|
831
840
|
var app = Application.currentApplication();
|
|
832
841
|
app.includeStandardAdditions = true;
|
|
833
|
-
var path =
|
|
842
|
+
var path = ${pathLiteral};
|
|
834
843
|
var url = $.NSURL.fileURLWithPath(path);
|
|
835
844
|
var image = $.NSImage.alloc.initWithContentsOfURL(url);
|
|
836
845
|
if (!image || !image.isValid) {
|
|
@@ -914,10 +923,10 @@ export class MacOSPlatform {
|
|
|
914
923
|
const effectiveApp = app || this.activeTarget?.appName;
|
|
915
924
|
const maxDepth = Math.min(depth || 5, 10);
|
|
916
925
|
const maxResults = Math.min(Math.max(options.maxResults ?? 50, 1), 200);
|
|
917
|
-
const
|
|
918
|
-
const
|
|
919
|
-
const
|
|
920
|
-
const
|
|
926
|
+
const appLiteral = JSON.stringify(effectiveApp || "");
|
|
927
|
+
const textLiteral = text ? JSON.stringify(text) : "null";
|
|
928
|
+
const roleLiteral = role ? JSON.stringify(role) : "null";
|
|
929
|
+
const valueLiteral = value ? JSON.stringify(value) : "null";
|
|
921
930
|
// Pre-compile regex on TS side to validate syntax before passing to JXA
|
|
922
931
|
if (text && textMode === "regex") {
|
|
923
932
|
try {
|
|
@@ -941,11 +950,7 @@ export class MacOSPlatform {
|
|
|
941
950
|
const startTime = Date.now();
|
|
942
951
|
const jxaScript = `
|
|
943
952
|
var se = Application('System Events');
|
|
944
|
-
|
|
945
|
-
try { return elem.uiElements(); } catch(e1) {
|
|
946
|
-
try { return elem.elements(); } catch(e2) { return []; }
|
|
947
|
-
}
|
|
948
|
-
}
|
|
953
|
+
${jxaChildElements()}
|
|
949
954
|
var results = [];
|
|
950
955
|
var scannedCount = 0;
|
|
951
956
|
var matchedCount = 0;
|
|
@@ -953,22 +958,13 @@ export class MacOSPlatform {
|
|
|
953
958
|
var maxResults = ${maxResults};
|
|
954
959
|
var includeBounds = ${includeBounds ? "true" : "false"};
|
|
955
960
|
var visibleOnly = ${visibleOnly ? "true" : "false"};
|
|
956
|
-
var textMode =
|
|
961
|
+
var textMode = ${JSON.stringify(textMode)};
|
|
957
962
|
|
|
958
|
-
var textFilter = ${
|
|
959
|
-
var roleFilter = ${
|
|
960
|
-
var valueFilter = ${
|
|
963
|
+
var textFilter = ${textLiteral};
|
|
964
|
+
var roleFilter = ${roleLiteral};
|
|
965
|
+
var valueFilter = ${valueLiteral};
|
|
961
966
|
|
|
962
|
-
|
|
963
|
-
try {
|
|
964
|
-
var pos = elem.position();
|
|
965
|
-
var sz = elem.size();
|
|
966
|
-
if (!pos || !sz) return false;
|
|
967
|
-
return sz[0] > 0 && sz[1] > 0 && pos[0] > -10000 && pos[1] > -10000;
|
|
968
|
-
} catch(e) {
|
|
969
|
-
return false;
|
|
970
|
-
}
|
|
971
|
-
}
|
|
967
|
+
${jxaIsVisible()}
|
|
972
968
|
|
|
973
969
|
// Shared filter helper. textMatches and valueMatches used to be near
|
|
974
970
|
// copies of the same three-branch dispatch (contains / exact / regex);
|
|
@@ -1035,15 +1031,7 @@ export class MacOSPlatform {
|
|
|
1035
1031
|
return true;
|
|
1036
1032
|
}
|
|
1037
1033
|
|
|
1038
|
-
|
|
1039
|
-
try {
|
|
1040
|
-
var pos = elem.position();
|
|
1041
|
-
var sz = elem.size();
|
|
1042
|
-
return {x: pos[0] || 0, y: pos[1] || 0, width: sz[0] || 0, height: sz[1] || 0};
|
|
1043
|
-
} catch(e) {
|
|
1044
|
-
return {x: 0, y: 0, width: 0, height: 0};
|
|
1045
|
-
}
|
|
1046
|
-
}
|
|
1034
|
+
${jxaGetBounds()}
|
|
1047
1035
|
|
|
1048
1036
|
function traverse(elem, path, currentDepth) {
|
|
1049
1037
|
if (resultCount[0] >= maxResults) return;
|
|
@@ -1094,11 +1082,11 @@ export class MacOSPlatform {
|
|
|
1094
1082
|
}
|
|
1095
1083
|
|
|
1096
1084
|
try {
|
|
1097
|
-
if (
|
|
1098
|
-
var proc = se.processes[
|
|
1085
|
+
if (${appLiteral}) {
|
|
1086
|
+
var proc = se.processes[${appLiteral}]();
|
|
1099
1087
|
var wins = proc.windows();
|
|
1100
1088
|
for (var w = 0; w < wins.length && resultCount[0] < maxResults; w++) {
|
|
1101
|
-
traverse(wins[w],
|
|
1089
|
+
traverse(wins[w], ${appLiteral} + "/win" + w, 0);
|
|
1102
1090
|
}
|
|
1103
1091
|
} else {
|
|
1104
1092
|
var procs = se.processes();
|
|
@@ -1183,9 +1171,9 @@ export class MacOSPlatform {
|
|
|
1183
1171
|
}
|
|
1184
1172
|
async clickElement(elementId, app) {
|
|
1185
1173
|
this.evictExpiredCacheEntries();
|
|
1186
|
-
const
|
|
1174
|
+
const elementIdLiteral = JSON.stringify(elementId);
|
|
1187
1175
|
const effectiveApp = app || this.activeTarget?.appName;
|
|
1188
|
-
const
|
|
1176
|
+
const appLiteral = JSON.stringify(effectiveApp || "");
|
|
1189
1177
|
const cached = this.elementCache.get(elementId);
|
|
1190
1178
|
if (cached && this.isCacheEntryExpired(cached)) {
|
|
1191
1179
|
this.elementCache.delete(elementId);
|
|
@@ -1194,173 +1182,12 @@ export class MacOSPlatform {
|
|
|
1194
1182
|
const jxaScript = `
|
|
1195
1183
|
var se = Application('System Events');
|
|
1196
1184
|
var _result = null;
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
}
|
|
1201
|
-
}
|
|
1202
|
-
var elemPath = "${escapedElementId}";
|
|
1203
|
-
var appName = "${escapedApp}";
|
|
1185
|
+
${jxaElementActionHelpers()}
|
|
1186
|
+
var elemPath = ${elementIdLiteral};
|
|
1187
|
+
var appName = ${appLiteral};
|
|
1204
1188
|
var cached = ${cachedJson};
|
|
1205
1189
|
|
|
1206
|
-
|
|
1207
|
-
var parts = path.split('/');
|
|
1208
|
-
if (parts.length < 2) return null;
|
|
1209
|
-
|
|
1210
|
-
var procName = parts[0];
|
|
1211
|
-
var winPart = parts[1];
|
|
1212
|
-
var winIdx = 0;
|
|
1213
|
-
var match = winPart.match(/^win(\\\\d+)$/);
|
|
1214
|
-
if (match) {
|
|
1215
|
-
winIdx = parseInt(match[1]);
|
|
1216
|
-
}
|
|
1217
|
-
|
|
1218
|
-
try {
|
|
1219
|
-
var proc = se.processes[procName]();
|
|
1220
|
-
var wins = proc.windows();
|
|
1221
|
-
if (winIdx >= wins.length) return null;
|
|
1222
|
-
var current = wins[winIdx];
|
|
1223
|
-
|
|
1224
|
-
for (var i = 2; i < parts.length; i++) {
|
|
1225
|
-
var idx = parseInt(parts[i]);
|
|
1226
|
-
if (isNaN(idx)) return null;
|
|
1227
|
-
try {
|
|
1228
|
-
var kids = childElements(current);
|
|
1229
|
-
if (idx >= kids.length) return null;
|
|
1230
|
-
current = kids[idx];
|
|
1231
|
-
} catch(e) { return null; }
|
|
1232
|
-
}
|
|
1233
|
-
return current;
|
|
1234
|
-
} catch(e) { return null; }
|
|
1235
|
-
}
|
|
1236
|
-
|
|
1237
|
-
function elemString(elem, getter) {
|
|
1238
|
-
try {
|
|
1239
|
-
var value = getter(elem);
|
|
1240
|
-
return value === undefined || value === null ? '' : String(value);
|
|
1241
|
-
} catch(e) {
|
|
1242
|
-
return '';
|
|
1243
|
-
}
|
|
1244
|
-
}
|
|
1245
|
-
|
|
1246
|
-
function getBounds(elem) {
|
|
1247
|
-
try {
|
|
1248
|
-
var pos = elem.position();
|
|
1249
|
-
var sz = elem.size();
|
|
1250
|
-
return {x: pos[0] || 0, y: pos[1] || 0, width: sz[0] || 0, height: sz[1] || 0};
|
|
1251
|
-
} catch(e) {
|
|
1252
|
-
return {x: 0, y: 0, width: 0, height: 0};
|
|
1253
|
-
}
|
|
1254
|
-
}
|
|
1255
|
-
|
|
1256
|
-
function descriptorMatches(elem) {
|
|
1257
|
-
if (!cached) return true;
|
|
1258
|
-
var role = elemString(elem, function(e) { return e.role(); });
|
|
1259
|
-
var name = elemString(elem, function(e) { return e.name(); });
|
|
1260
|
-
var desc = elemString(elem, function(e) { return e.description(); });
|
|
1261
|
-
var value = elemString(elem, function(e) { return e.value(); });
|
|
1262
|
-
if (cached.role && role && role !== cached.role) return false;
|
|
1263
|
-
if (cached.name && name && name !== cached.name) return false;
|
|
1264
|
-
if (cached.value && value && value !== cached.value) return false;
|
|
1265
|
-
if (cached.description && desc && desc !== cached.description) return false;
|
|
1266
|
-
return true;
|
|
1267
|
-
}
|
|
1268
|
-
|
|
1269
|
-
function scoreEquivalent(elem) {
|
|
1270
|
-
if (!cached) return -1;
|
|
1271
|
-
var score = 0;
|
|
1272
|
-
var role = elemString(elem, function(e) { return e.role(); });
|
|
1273
|
-
var name = elemString(elem, function(e) { return e.name(); });
|
|
1274
|
-
var desc = elemString(elem, function(e) { return e.description(); });
|
|
1275
|
-
var value = elemString(elem, function(e) { return e.value(); });
|
|
1276
|
-
var subrole = elemString(elem, function(e) { return e.subrole(); });
|
|
1277
|
-
var identifier = elemString(elem, function(e) { return e.identifier(); });
|
|
1278
|
-
if (cached.role && role === cached.role) score += 4;
|
|
1279
|
-
if (cached.name && name === cached.name) score += 4;
|
|
1280
|
-
if (cached.value && value === cached.value) score += 3;
|
|
1281
|
-
if (cached.description && desc === cached.description) score += 2;
|
|
1282
|
-
if (cached.subrole && subrole === cached.subrole) score += 2;
|
|
1283
|
-
if (cached.identifier && identifier === cached.identifier) score += 3;
|
|
1284
|
-
var b = getBounds(elem);
|
|
1285
|
-
if (cached.bounds) {
|
|
1286
|
-
var cx = b.x + b.width / 2;
|
|
1287
|
-
var cy = b.y + b.height / 2;
|
|
1288
|
-
var ocx = cached.bounds.x + cached.bounds.width / 2;
|
|
1289
|
-
var ocy = cached.bounds.y + cached.bounds.height / 2;
|
|
1290
|
-
var distance = Math.sqrt(Math.pow(cx - ocx, 2) + Math.pow(cy - ocy, 2));
|
|
1291
|
-
if (distance < 8) score += 4;
|
|
1292
|
-
else if (distance < 40) score += 2;
|
|
1293
|
-
else if (distance < 120) score += 1;
|
|
1294
|
-
}
|
|
1295
|
-
return score;
|
|
1296
|
-
}
|
|
1297
|
-
|
|
1298
|
-
function refetchEquivalent() {
|
|
1299
|
-
if (!cached) return null;
|
|
1300
|
-
var targetApp = appName || cached.appName || '';
|
|
1301
|
-
var best = null;
|
|
1302
|
-
var bestScore = 0;
|
|
1303
|
-
var visited = [0];
|
|
1304
|
-
function visit(elem, depth) {
|
|
1305
|
-
if (visited[0] > 350 || depth > 10) return;
|
|
1306
|
-
visited[0]++;
|
|
1307
|
-
var score = scoreEquivalent(elem);
|
|
1308
|
-
if (score > bestScore) {
|
|
1309
|
-
best = elem;
|
|
1310
|
-
bestScore = score;
|
|
1311
|
-
}
|
|
1312
|
-
try {
|
|
1313
|
-
var kids = childElements(elem);
|
|
1314
|
-
for (var i = 0; i < kids.length; i++) visit(kids[i], depth + 1);
|
|
1315
|
-
} catch(e) {}
|
|
1316
|
-
}
|
|
1317
|
-
try {
|
|
1318
|
-
if (targetApp) {
|
|
1319
|
-
var proc = se.processes[targetApp]();
|
|
1320
|
-
var wins = proc.windows();
|
|
1321
|
-
for (var w = 0; w < wins.length; w++) visit(wins[w], 0);
|
|
1322
|
-
} else {
|
|
1323
|
-
var procs = se.processes();
|
|
1324
|
-
for (var p = 0; p < procs.length; p++) {
|
|
1325
|
-
try {
|
|
1326
|
-
var wins2 = procs[p].windows();
|
|
1327
|
-
for (var w2 = 0; w2 < wins2.length; w2++) visit(wins2[w2], 0);
|
|
1328
|
-
} catch(e2) {}
|
|
1329
|
-
}
|
|
1330
|
-
}
|
|
1331
|
-
} catch(e) {}
|
|
1332
|
-
return bestScore >= 6 ? best : null;
|
|
1333
|
-
}
|
|
1334
|
-
|
|
1335
|
-
var elem = null;
|
|
1336
|
-
|
|
1337
|
-
if (appName) {
|
|
1338
|
-
try {
|
|
1339
|
-
var proc = se.processes[appName]();
|
|
1340
|
-
var wins = proc.windows();
|
|
1341
|
-
var parts = elemPath.split('/');
|
|
1342
|
-
var winIdx = 0;
|
|
1343
|
-
var match = parts[0].match(/^win(\\\\d+)$/);
|
|
1344
|
-
if (match) winIdx = parseInt(match[1]);
|
|
1345
|
-
if (winIdx < wins.length) {
|
|
1346
|
-
var current = wins[winIdx];
|
|
1347
|
-
for (var i = 1; i < parts.length; i++) {
|
|
1348
|
-
var idx = parseInt(parts[i]);
|
|
1349
|
-
if (isNaN(idx)) break;
|
|
1350
|
-
try {
|
|
1351
|
-
var kids = childElements(current);
|
|
1352
|
-
if (idx >= kids.length) break;
|
|
1353
|
-
current = kids[idx];
|
|
1354
|
-
} catch(e) { break; }
|
|
1355
|
-
}
|
|
1356
|
-
elem = current;
|
|
1357
|
-
}
|
|
1358
|
-
} catch(e) {}
|
|
1359
|
-
}
|
|
1360
|
-
|
|
1361
|
-
if (!elem) {
|
|
1362
|
-
elem = resolveElementByFullPath(elemPath);
|
|
1363
|
-
}
|
|
1190
|
+
var elem = resolveElementInApp(elemPath, appName) || resolveElementByFullPath(elemPath);
|
|
1364
1191
|
|
|
1365
1192
|
if (elem && !descriptorMatches(elem)) {
|
|
1366
1193
|
elem = refetchEquivalent() || elem;
|
|
@@ -1415,10 +1242,10 @@ export class MacOSPlatform {
|
|
|
1415
1242
|
}
|
|
1416
1243
|
async typeInElement(elementId, text, app, clearFirst) {
|
|
1417
1244
|
this.evictExpiredCacheEntries();
|
|
1418
|
-
const
|
|
1245
|
+
const textLiteral = JSON.stringify(text);
|
|
1419
1246
|
const effectiveApp = app || this.activeTarget?.appName;
|
|
1420
|
-
const
|
|
1421
|
-
const
|
|
1247
|
+
const appLiteral = JSON.stringify(effectiveApp || "");
|
|
1248
|
+
const elementIdLiteral = JSON.stringify(elementId);
|
|
1422
1249
|
const cached = this.elementCache.get(elementId);
|
|
1423
1250
|
if (cached && this.isCacheEntryExpired(cached)) {
|
|
1424
1251
|
this.elementCache.delete(elementId);
|
|
@@ -1427,175 +1254,14 @@ export class MacOSPlatform {
|
|
|
1427
1254
|
const jxaScript = `
|
|
1428
1255
|
var se = Application('System Events');
|
|
1429
1256
|
var _result = null;
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
}
|
|
1435
|
-
var elemPath = "${escapedElementId}";
|
|
1436
|
-
var appName = "${escapedApp}";
|
|
1437
|
-
var textToType = "${escapedText}";
|
|
1257
|
+
${jxaElementActionHelpers()}
|
|
1258
|
+
var elemPath = ${elementIdLiteral};
|
|
1259
|
+
var appName = ${appLiteral};
|
|
1260
|
+
var textToType = ${textLiteral};
|
|
1438
1261
|
var shouldClear = ${clearFirst ? "true" : "false"};
|
|
1439
1262
|
var cached = ${cachedJson};
|
|
1440
1263
|
|
|
1441
|
-
|
|
1442
|
-
var parts = path.split('/');
|
|
1443
|
-
if (parts.length < 2) return null;
|
|
1444
|
-
|
|
1445
|
-
var procName = parts[0];
|
|
1446
|
-
var winPart = parts[1];
|
|
1447
|
-
var winIdx = 0;
|
|
1448
|
-
var match = winPart.match(/^win(\\\\d+)$/);
|
|
1449
|
-
if (match) {
|
|
1450
|
-
winIdx = parseInt(match[1]);
|
|
1451
|
-
}
|
|
1452
|
-
|
|
1453
|
-
try {
|
|
1454
|
-
var proc = se.processes[procName]();
|
|
1455
|
-
var wins = proc.windows();
|
|
1456
|
-
if (winIdx >= wins.length) return null;
|
|
1457
|
-
var current = wins[winIdx];
|
|
1458
|
-
|
|
1459
|
-
for (var i = 2; i < parts.length; i++) {
|
|
1460
|
-
var idx = parseInt(parts[i]);
|
|
1461
|
-
if (isNaN(idx)) return null;
|
|
1462
|
-
try {
|
|
1463
|
-
var kids = childElements(current);
|
|
1464
|
-
if (idx >= kids.length) return null;
|
|
1465
|
-
current = kids[idx];
|
|
1466
|
-
} catch(e) { return null; }
|
|
1467
|
-
}
|
|
1468
|
-
return current;
|
|
1469
|
-
} catch(e) { return null; }
|
|
1470
|
-
}
|
|
1471
|
-
|
|
1472
|
-
function elemString(elem, getter) {
|
|
1473
|
-
try {
|
|
1474
|
-
var value = getter(elem);
|
|
1475
|
-
return value === undefined || value === null ? '' : String(value);
|
|
1476
|
-
} catch(e) {
|
|
1477
|
-
return '';
|
|
1478
|
-
}
|
|
1479
|
-
}
|
|
1480
|
-
|
|
1481
|
-
function getBounds(elem) {
|
|
1482
|
-
try {
|
|
1483
|
-
var pos = elem.position();
|
|
1484
|
-
var sz = elem.size();
|
|
1485
|
-
return {x: pos[0] || 0, y: pos[1] || 0, width: sz[0] || 0, height: sz[1] || 0};
|
|
1486
|
-
} catch(e) {
|
|
1487
|
-
return {x: 0, y: 0, width: 0, height: 0};
|
|
1488
|
-
}
|
|
1489
|
-
}
|
|
1490
|
-
|
|
1491
|
-
function descriptorMatches(elem) {
|
|
1492
|
-
if (!cached) return true;
|
|
1493
|
-
var role = elemString(elem, function(e) { return e.role(); });
|
|
1494
|
-
var name = elemString(elem, function(e) { return e.name(); });
|
|
1495
|
-
var desc = elemString(elem, function(e) { return e.description(); });
|
|
1496
|
-
var value = elemString(elem, function(e) { return e.value(); });
|
|
1497
|
-
if (cached.role && role && role !== cached.role) return false;
|
|
1498
|
-
if (cached.name && name && name !== cached.name) return false;
|
|
1499
|
-
if (cached.value && value && value !== cached.value) return false;
|
|
1500
|
-
if (cached.description && desc && desc !== cached.description) return false;
|
|
1501
|
-
return true;
|
|
1502
|
-
}
|
|
1503
|
-
|
|
1504
|
-
function scoreEquivalent(elem) {
|
|
1505
|
-
if (!cached) return -1;
|
|
1506
|
-
var score = 0;
|
|
1507
|
-
var role = elemString(elem, function(e) { return e.role(); });
|
|
1508
|
-
var name = elemString(elem, function(e) { return e.name(); });
|
|
1509
|
-
var desc = elemString(elem, function(e) { return e.description(); });
|
|
1510
|
-
var value = elemString(elem, function(e) { return e.value(); });
|
|
1511
|
-
var subrole = elemString(elem, function(e) { return e.subrole(); });
|
|
1512
|
-
var identifier = elemString(elem, function(e) { return e.identifier(); });
|
|
1513
|
-
if (cached.role && role === cached.role) score += 4;
|
|
1514
|
-
if (cached.name && name === cached.name) score += 4;
|
|
1515
|
-
if (cached.value && value === cached.value) score += 3;
|
|
1516
|
-
if (cached.description && desc === cached.description) score += 2;
|
|
1517
|
-
if (cached.subrole && subrole === cached.subrole) score += 2;
|
|
1518
|
-
if (cached.identifier && identifier === cached.identifier) score += 3;
|
|
1519
|
-
var b = getBounds(elem);
|
|
1520
|
-
if (cached.bounds) {
|
|
1521
|
-
var cx = b.x + b.width / 2;
|
|
1522
|
-
var cy = b.y + b.height / 2;
|
|
1523
|
-
var ocx = cached.bounds.x + cached.bounds.width / 2;
|
|
1524
|
-
var ocy = cached.bounds.y + cached.bounds.height / 2;
|
|
1525
|
-
var distance = Math.sqrt(Math.pow(cx - ocx, 2) + Math.pow(cy - ocy, 2));
|
|
1526
|
-
if (distance < 8) score += 4;
|
|
1527
|
-
else if (distance < 40) score += 2;
|
|
1528
|
-
else if (distance < 120) score += 1;
|
|
1529
|
-
}
|
|
1530
|
-
return score;
|
|
1531
|
-
}
|
|
1532
|
-
|
|
1533
|
-
function refetchEquivalent() {
|
|
1534
|
-
if (!cached) return null;
|
|
1535
|
-
var targetApp = appName || cached.appName || '';
|
|
1536
|
-
var best = null;
|
|
1537
|
-
var bestScore = 0;
|
|
1538
|
-
var visited = [0];
|
|
1539
|
-
function visit(elem, depth) {
|
|
1540
|
-
if (visited[0] > 350 || depth > 10) return;
|
|
1541
|
-
visited[0]++;
|
|
1542
|
-
var score = scoreEquivalent(elem);
|
|
1543
|
-
if (score > bestScore) {
|
|
1544
|
-
best = elem;
|
|
1545
|
-
bestScore = score;
|
|
1546
|
-
}
|
|
1547
|
-
try {
|
|
1548
|
-
var kids = childElements(elem);
|
|
1549
|
-
for (var i = 0; i < kids.length; i++) visit(kids[i], depth + 1);
|
|
1550
|
-
} catch(e) {}
|
|
1551
|
-
}
|
|
1552
|
-
try {
|
|
1553
|
-
if (targetApp) {
|
|
1554
|
-
var proc = se.processes[targetApp]();
|
|
1555
|
-
var wins = proc.windows();
|
|
1556
|
-
for (var w = 0; w < wins.length; w++) visit(wins[w], 0);
|
|
1557
|
-
} else {
|
|
1558
|
-
var procs = se.processes();
|
|
1559
|
-
for (var p = 0; p < procs.length; p++) {
|
|
1560
|
-
try {
|
|
1561
|
-
var wins2 = procs[p].windows();
|
|
1562
|
-
for (var w2 = 0; w2 < wins2.length; w2++) visit(wins2[w2], 0);
|
|
1563
|
-
} catch(e2) {}
|
|
1564
|
-
}
|
|
1565
|
-
}
|
|
1566
|
-
} catch(e) {}
|
|
1567
|
-
return bestScore >= 6 ? best : null;
|
|
1568
|
-
}
|
|
1569
|
-
|
|
1570
|
-
var elem = null;
|
|
1571
|
-
|
|
1572
|
-
if (appName) {
|
|
1573
|
-
try {
|
|
1574
|
-
var proc = se.processes[appName]();
|
|
1575
|
-
var wins = proc.windows();
|
|
1576
|
-
var parts = elemPath.split('/');
|
|
1577
|
-
var winIdx = 0;
|
|
1578
|
-
var match = parts[0].match(/^win(\\\\d+)$/);
|
|
1579
|
-
if (match) winIdx = parseInt(match[1]);
|
|
1580
|
-
if (winIdx < wins.length) {
|
|
1581
|
-
var current = wins[winIdx];
|
|
1582
|
-
for (var i = 1; i < parts.length; i++) {
|
|
1583
|
-
var idx = parseInt(parts[i]);
|
|
1584
|
-
if (isNaN(idx)) break;
|
|
1585
|
-
try {
|
|
1586
|
-
var kids = childElements(current);
|
|
1587
|
-
if (idx >= kids.length) break;
|
|
1588
|
-
current = kids[idx];
|
|
1589
|
-
} catch(e) { break; }
|
|
1590
|
-
}
|
|
1591
|
-
elem = current;
|
|
1592
|
-
}
|
|
1593
|
-
} catch(e) {}
|
|
1594
|
-
}
|
|
1595
|
-
|
|
1596
|
-
if (!elem) {
|
|
1597
|
-
elem = resolveElementByFullPath(elemPath);
|
|
1598
|
-
}
|
|
1264
|
+
var elem = resolveElementInApp(elemPath, appName) || resolveElementByFullPath(elemPath);
|
|
1599
1265
|
|
|
1600
1266
|
if (elem && !descriptorMatches(elem)) {
|
|
1601
1267
|
elem = refetchEquivalent() || elem;
|
|
@@ -1691,170 +1357,12 @@ export class MacOSPlatform {
|
|
|
1691
1357
|
const jxaScript = `
|
|
1692
1358
|
var se = Application('System Events');
|
|
1693
1359
|
var _result = null;
|
|
1694
|
-
|
|
1695
|
-
try { return elem.uiElements(); } catch(e1) {
|
|
1696
|
-
try { return elem.elements(); } catch(e2) { return []; }
|
|
1697
|
-
}
|
|
1698
|
-
}
|
|
1360
|
+
${jxaElementActionHelpers()}
|
|
1699
1361
|
var elemPath = ${elementIdLiteral};
|
|
1700
1362
|
var appName = ${appLiteral};
|
|
1701
1363
|
var valueToSet = ${valueLiteral};
|
|
1702
1364
|
var cached = ${cachedJson};
|
|
1703
1365
|
|
|
1704
|
-
function resolveElementByFullPath(path) {
|
|
1705
|
-
var parts = path.split('/');
|
|
1706
|
-
if (parts.length < 2) return null;
|
|
1707
|
-
|
|
1708
|
-
var procName = parts[0];
|
|
1709
|
-
var winPart = parts[1];
|
|
1710
|
-
var winIdx = 0;
|
|
1711
|
-
var match = winPart.match(/^win(\\\\d+)$/);
|
|
1712
|
-
if (match) winIdx = parseInt(match[1]);
|
|
1713
|
-
|
|
1714
|
-
try {
|
|
1715
|
-
var proc = se.processes[procName]();
|
|
1716
|
-
var wins = proc.windows();
|
|
1717
|
-
if (winIdx >= wins.length) return null;
|
|
1718
|
-
var current = wins[winIdx];
|
|
1719
|
-
|
|
1720
|
-
for (var i = 2; i < parts.length; i++) {
|
|
1721
|
-
var idx = parseInt(parts[i]);
|
|
1722
|
-
if (isNaN(idx)) return null;
|
|
1723
|
-
try {
|
|
1724
|
-
var kids = childElements(current);
|
|
1725
|
-
if (idx >= kids.length) return null;
|
|
1726
|
-
current = kids[idx];
|
|
1727
|
-
} catch(e) { return null; }
|
|
1728
|
-
}
|
|
1729
|
-
return current;
|
|
1730
|
-
} catch(e) { return null; }
|
|
1731
|
-
}
|
|
1732
|
-
|
|
1733
|
-
function resolveElementInApp(path, targetApp) {
|
|
1734
|
-
if (!targetApp) return null;
|
|
1735
|
-
var parts = path.split('/');
|
|
1736
|
-
var start = parts[0] === targetApp ? 1 : 0;
|
|
1737
|
-
var winPart = parts[start] || 'win0';
|
|
1738
|
-
var winIdx = 0;
|
|
1739
|
-
var match = winPart.match(/^win(\\\\d+)$/);
|
|
1740
|
-
if (match) winIdx = parseInt(match[1]);
|
|
1741
|
-
|
|
1742
|
-
try {
|
|
1743
|
-
var proc = se.processes[targetApp]();
|
|
1744
|
-
var wins = proc.windows();
|
|
1745
|
-
if (winIdx >= wins.length) return null;
|
|
1746
|
-
var current = wins[winIdx];
|
|
1747
|
-
for (var i = start + 1; i < parts.length; i++) {
|
|
1748
|
-
var idx = parseInt(parts[i]);
|
|
1749
|
-
if (isNaN(idx)) return null;
|
|
1750
|
-
try {
|
|
1751
|
-
var kids = childElements(current);
|
|
1752
|
-
if (idx >= kids.length) return null;
|
|
1753
|
-
current = kids[idx];
|
|
1754
|
-
} catch(e) { return null; }
|
|
1755
|
-
}
|
|
1756
|
-
return current;
|
|
1757
|
-
} catch(e) { return null; }
|
|
1758
|
-
}
|
|
1759
|
-
|
|
1760
|
-
function elemString(elem, getter) {
|
|
1761
|
-
try {
|
|
1762
|
-
var value = getter(elem);
|
|
1763
|
-
return value === undefined || value === null ? '' : String(value);
|
|
1764
|
-
} catch(e) {
|
|
1765
|
-
return '';
|
|
1766
|
-
}
|
|
1767
|
-
}
|
|
1768
|
-
|
|
1769
|
-
function getBounds(elem) {
|
|
1770
|
-
try {
|
|
1771
|
-
var pos = elem.position();
|
|
1772
|
-
var sz = elem.size();
|
|
1773
|
-
return {x: pos[0] || 0, y: pos[1] || 0, width: sz[0] || 0, height: sz[1] || 0};
|
|
1774
|
-
} catch(e) {
|
|
1775
|
-
return {x: 0, y: 0, width: 0, height: 0};
|
|
1776
|
-
}
|
|
1777
|
-
}
|
|
1778
|
-
|
|
1779
|
-
function descriptorMatches(elem) {
|
|
1780
|
-
if (!cached) return true;
|
|
1781
|
-
var role = elemString(elem, function(e) { return e.role(); });
|
|
1782
|
-
var name = elemString(elem, function(e) { return e.name(); });
|
|
1783
|
-
var desc = elemString(elem, function(e) { return e.description(); });
|
|
1784
|
-
var value = elemString(elem, function(e) { return e.value(); });
|
|
1785
|
-
if (cached.role && role && role !== cached.role) return false;
|
|
1786
|
-
if (cached.name && name && name !== cached.name) return false;
|
|
1787
|
-
if (cached.value && value && value !== cached.value) return false;
|
|
1788
|
-
if (cached.description && desc && desc !== cached.description) return false;
|
|
1789
|
-
return true;
|
|
1790
|
-
}
|
|
1791
|
-
|
|
1792
|
-
function scoreEquivalent(elem) {
|
|
1793
|
-
if (!cached) return -1;
|
|
1794
|
-
var score = 0;
|
|
1795
|
-
var role = elemString(elem, function(e) { return e.role(); });
|
|
1796
|
-
var name = elemString(elem, function(e) { return e.name(); });
|
|
1797
|
-
var desc = elemString(elem, function(e) { return e.description(); });
|
|
1798
|
-
var value = elemString(elem, function(e) { return e.value(); });
|
|
1799
|
-
var subrole = elemString(elem, function(e) { return e.subrole(); });
|
|
1800
|
-
var identifier = elemString(elem, function(e) { return e.identifier(); });
|
|
1801
|
-
if (cached.role && role === cached.role) score += 4;
|
|
1802
|
-
if (cached.name && name === cached.name) score += 4;
|
|
1803
|
-
if (cached.value && value === cached.value) score += 3;
|
|
1804
|
-
if (cached.description && desc === cached.description) score += 2;
|
|
1805
|
-
if (cached.subrole && subrole === cached.subrole) score += 2;
|
|
1806
|
-
if (cached.identifier && identifier === cached.identifier) score += 3;
|
|
1807
|
-
var b = getBounds(elem);
|
|
1808
|
-
if (cached.bounds) {
|
|
1809
|
-
var cx = b.x + b.width / 2;
|
|
1810
|
-
var cy = b.y + b.height / 2;
|
|
1811
|
-
var ocx = cached.bounds.x + cached.bounds.width / 2;
|
|
1812
|
-
var ocy = cached.bounds.y + cached.bounds.height / 2;
|
|
1813
|
-
var distance = Math.sqrt(Math.pow(cx - ocx, 2) + Math.pow(cy - ocy, 2));
|
|
1814
|
-
if (distance < 8) score += 4;
|
|
1815
|
-
else if (distance < 40) score += 2;
|
|
1816
|
-
else if (distance < 120) score += 1;
|
|
1817
|
-
}
|
|
1818
|
-
return score;
|
|
1819
|
-
}
|
|
1820
|
-
|
|
1821
|
-
function refetchEquivalent() {
|
|
1822
|
-
if (!cached) return null;
|
|
1823
|
-
var targetApp = appName || cached.appName || '';
|
|
1824
|
-
var best = null;
|
|
1825
|
-
var bestScore = 0;
|
|
1826
|
-
var visited = [0];
|
|
1827
|
-
function visit(elem, depth) {
|
|
1828
|
-
if (visited[0] > 350 || depth > 10) return;
|
|
1829
|
-
visited[0]++;
|
|
1830
|
-
var score = scoreEquivalent(elem);
|
|
1831
|
-
if (score > bestScore) {
|
|
1832
|
-
best = elem;
|
|
1833
|
-
bestScore = score;
|
|
1834
|
-
}
|
|
1835
|
-
try {
|
|
1836
|
-
var kids = childElements(elem);
|
|
1837
|
-
for (var i = 0; i < kids.length; i++) visit(kids[i], depth + 1);
|
|
1838
|
-
} catch(e) {}
|
|
1839
|
-
}
|
|
1840
|
-
try {
|
|
1841
|
-
if (targetApp) {
|
|
1842
|
-
var proc = se.processes[targetApp]();
|
|
1843
|
-
var wins = proc.windows();
|
|
1844
|
-
for (var w = 0; w < wins.length; w++) visit(wins[w], 0);
|
|
1845
|
-
} else {
|
|
1846
|
-
var procs = se.processes();
|
|
1847
|
-
for (var p = 0; p < procs.length; p++) {
|
|
1848
|
-
try {
|
|
1849
|
-
var wins2 = procs[p].windows();
|
|
1850
|
-
for (var w2 = 0; w2 < wins2.length; w2++) visit(wins2[w2], 0);
|
|
1851
|
-
} catch(e2) {}
|
|
1852
|
-
}
|
|
1853
|
-
}
|
|
1854
|
-
} catch(e) {}
|
|
1855
|
-
return bestScore >= 6 ? best : null;
|
|
1856
|
-
}
|
|
1857
|
-
|
|
1858
1366
|
var elem = resolveElementInApp(elemPath, appName) || resolveElementByFullPath(elemPath);
|
|
1859
1367
|
if (elem && !descriptorMatches(elem)) {
|
|
1860
1368
|
elem = refetchEquivalent() || elem;
|