@qontinui/ui-bridge 0.1.1 → 0.2.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/ai/index.d.mts +893 -0
- package/dist/ai/index.d.ts +893 -0
- package/dist/ai/index.js +3897 -0
- package/dist/ai/index.js.map +1 -0
- package/dist/ai/index.mjs +3839 -0
- package/dist/ai/index.mjs.map +1 -0
- package/dist/control/index.d.mts +5 -4
- package/dist/control/index.d.ts +5 -4
- package/dist/core/index.d.mts +6 -4
- package/dist/core/index.d.ts +6 -4
- package/dist/core/index.js +581 -4
- package/dist/core/index.js.map +1 -1
- package/dist/core/index.mjs +581 -4
- package/dist/core/index.mjs.map +1 -1
- package/dist/debug/index.d.mts +3 -3
- package/dist/debug/index.d.ts +3 -3
- package/dist/index.d.mts +7 -5
- package/dist/index.d.ts +7 -5
- package/dist/index.js +4112 -12
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +4056 -13
- package/dist/index.mjs.map +1 -1
- package/dist/{metrics-QCnK0EFw.d.ts → metrics-C9XRi_mL.d.ts} +2 -2
- package/dist/{metrics-BCG7z7Aq.d.mts → metrics-NC3csD0R.d.mts} +2 -2
- package/dist/react/index.d.mts +6 -5
- package/dist/react/index.d.ts +6 -5
- package/dist/react/index.js +587 -10
- package/dist/react/index.js.map +1 -1
- package/dist/react/index.mjs +587 -10
- package/dist/react/index.mjs.map +1 -1
- package/dist/{registry-CT6BVVKr.d.mts → registry-CIEDjbQ9.d.ts} +22 -1
- package/dist/{registry-D4mQ01B3.d.ts → registry-SsSDq46X.d.mts} +22 -1
- package/dist/render-log/index.d.mts +1 -1
- package/dist/render-log/index.d.ts +1 -1
- package/dist/types-BvCfFuEV.d.ts +534 -0
- package/dist/types-CFT3Dnx4.d.mts +534 -0
- package/dist/{types-BpvpStn3.d.mts → types-CPMbN_Iw.d.mts} +8 -0
- package/dist/{types-BpvpStn3.d.ts → types-CPMbN_Iw.d.ts} +8 -0
- package/dist/{types-DdJD9yw5.d.mts → types-Dr6tH-bm.d.mts} +1 -1
- package/dist/{types-BDkXy5si.d.ts → types-oCTrRxSw.d.ts} +1 -1
- package/dist/{websocket-client-DupH0X7B.d.ts → websocket-client-CX4QJesI.d.ts} +1 -1
- package/dist/{websocket-client-B2LC9CYc.d.mts → websocket-client-C_Na0OSp.d.mts} +1 -1
- package/package.json +6 -1
package/dist/react/index.js
CHANGED
|
@@ -190,6 +190,376 @@ function findElementByIdentifier(identifier, root = document) {
|
|
|
190
190
|
return null;
|
|
191
191
|
}
|
|
192
192
|
|
|
193
|
+
// src/ai/fuzzy-matcher.ts
|
|
194
|
+
var DEFAULT_FUZZY_CONFIG = {
|
|
195
|
+
threshold: 0.7,
|
|
196
|
+
levenshteinWeight: 0.3,
|
|
197
|
+
jaroWinklerWeight: 0.4,
|
|
198
|
+
ngramWeight: 0.3,
|
|
199
|
+
ngramSize: 2,
|
|
200
|
+
caseSensitive: false,
|
|
201
|
+
ignoreWhitespace: true
|
|
202
|
+
};
|
|
203
|
+
function levenshteinDistance(s1, s2) {
|
|
204
|
+
const len1 = s1.length;
|
|
205
|
+
const len2 = s2.length;
|
|
206
|
+
const matrix = Array(len1 + 1).fill(null).map(() => Array(len2 + 1).fill(0));
|
|
207
|
+
for (let i = 0; i <= len1; i++) matrix[i][0] = i;
|
|
208
|
+
for (let j = 0; j <= len2; j++) matrix[0][j] = j;
|
|
209
|
+
for (let i = 1; i <= len1; i++) {
|
|
210
|
+
for (let j = 1; j <= len2; j++) {
|
|
211
|
+
const cost = s1[i - 1] === s2[j - 1] ? 0 : 1;
|
|
212
|
+
matrix[i][j] = Math.min(
|
|
213
|
+
matrix[i - 1][j] + 1,
|
|
214
|
+
// deletion
|
|
215
|
+
matrix[i][j - 1] + 1,
|
|
216
|
+
// insertion
|
|
217
|
+
matrix[i - 1][j - 1] + cost
|
|
218
|
+
// substitution
|
|
219
|
+
);
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
return matrix[len1][len2];
|
|
223
|
+
}
|
|
224
|
+
function levenshteinSimilarity(s1, s2) {
|
|
225
|
+
if (s1.length === 0 && s2.length === 0) return 1;
|
|
226
|
+
if (s1.length === 0 || s2.length === 0) return 0;
|
|
227
|
+
const distance = levenshteinDistance(s1, s2);
|
|
228
|
+
const maxLength = Math.max(s1.length, s2.length);
|
|
229
|
+
return 1 - distance / maxLength;
|
|
230
|
+
}
|
|
231
|
+
function jaroSimilarity(s1, s2) {
|
|
232
|
+
if (s1.length === 0 && s2.length === 0) return 1;
|
|
233
|
+
if (s1.length === 0 || s2.length === 0) return 0;
|
|
234
|
+
const matchDistance = Math.floor(Math.max(s1.length, s2.length) / 2) - 1;
|
|
235
|
+
const s1Matches = new Array(s1.length).fill(false);
|
|
236
|
+
const s2Matches = new Array(s2.length).fill(false);
|
|
237
|
+
let matches = 0;
|
|
238
|
+
let transpositions = 0;
|
|
239
|
+
for (let i = 0; i < s1.length; i++) {
|
|
240
|
+
const start = Math.max(0, i - matchDistance);
|
|
241
|
+
const end = Math.min(i + matchDistance + 1, s2.length);
|
|
242
|
+
for (let j = start; j < end; j++) {
|
|
243
|
+
if (s2Matches[j] || s1[i] !== s2[j]) continue;
|
|
244
|
+
s1Matches[i] = true;
|
|
245
|
+
s2Matches[j] = true;
|
|
246
|
+
matches++;
|
|
247
|
+
break;
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
if (matches === 0) return 0;
|
|
251
|
+
let k = 0;
|
|
252
|
+
for (let i = 0; i < s1.length; i++) {
|
|
253
|
+
if (!s1Matches[i]) continue;
|
|
254
|
+
while (!s2Matches[k]) k++;
|
|
255
|
+
if (s1[i] !== s2[k]) transpositions++;
|
|
256
|
+
k++;
|
|
257
|
+
}
|
|
258
|
+
return (matches / s1.length + matches / s2.length + (matches - transpositions / 2) / matches) / 3;
|
|
259
|
+
}
|
|
260
|
+
function jaroWinklerSimilarity(s1, s2, prefixScale = 0.1) {
|
|
261
|
+
const jaroSim = jaroSimilarity(s1, s2);
|
|
262
|
+
let prefixLength = 0;
|
|
263
|
+
const maxPrefix = Math.min(4, Math.min(s1.length, s2.length));
|
|
264
|
+
for (let i = 0; i < maxPrefix; i++) {
|
|
265
|
+
if (s1[i] === s2[i]) {
|
|
266
|
+
prefixLength++;
|
|
267
|
+
} else {
|
|
268
|
+
break;
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
return jaroSim + prefixLength * prefixScale * (1 - jaroSim);
|
|
272
|
+
}
|
|
273
|
+
function generateNgrams(s, n) {
|
|
274
|
+
const ngrams = /* @__PURE__ */ new Set();
|
|
275
|
+
if (s.length < n) {
|
|
276
|
+
ngrams.add(s);
|
|
277
|
+
return ngrams;
|
|
278
|
+
}
|
|
279
|
+
for (let i = 0; i <= s.length - n; i++) {
|
|
280
|
+
ngrams.add(s.substring(i, i + n));
|
|
281
|
+
}
|
|
282
|
+
return ngrams;
|
|
283
|
+
}
|
|
284
|
+
function ngramSimilarity(s1, s2, n = 2) {
|
|
285
|
+
if (s1.length === 0 && s2.length === 0) return 1;
|
|
286
|
+
if (s1.length === 0 || s2.length === 0) return 0;
|
|
287
|
+
const ngrams1 = generateNgrams(s1, n);
|
|
288
|
+
const ngrams2 = generateNgrams(s2, n);
|
|
289
|
+
let intersection = 0;
|
|
290
|
+
for (const ngram of ngrams1) {
|
|
291
|
+
if (ngrams2.has(ngram)) {
|
|
292
|
+
intersection++;
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
const union = ngrams1.size + ngrams2.size - intersection;
|
|
296
|
+
return union === 0 ? 0 : intersection / union;
|
|
297
|
+
}
|
|
298
|
+
function normalizeString(s, config = {}) {
|
|
299
|
+
let normalized = s;
|
|
300
|
+
if (!config.caseSensitive) {
|
|
301
|
+
normalized = normalized.toLowerCase();
|
|
302
|
+
}
|
|
303
|
+
if (config.ignoreWhitespace !== false) {
|
|
304
|
+
normalized = normalized.replace(/\s+/g, " ").trim();
|
|
305
|
+
}
|
|
306
|
+
return normalized;
|
|
307
|
+
}
|
|
308
|
+
function fuzzyMatch(source, target, config = {}) {
|
|
309
|
+
const finalConfig = { ...DEFAULT_FUZZY_CONFIG, ...config };
|
|
310
|
+
const normalizedSource = normalizeString(source, finalConfig);
|
|
311
|
+
const normalizedTarget = normalizeString(target, finalConfig);
|
|
312
|
+
const levenshteinScore = levenshteinSimilarity(normalizedSource, normalizedTarget);
|
|
313
|
+
const jaroWinklerScore = jaroWinklerSimilarity(normalizedSource, normalizedTarget);
|
|
314
|
+
const ngramScore = ngramSimilarity(normalizedSource, normalizedTarget, finalConfig.ngramSize);
|
|
315
|
+
const similarity = levenshteinScore * finalConfig.levenshteinWeight + jaroWinklerScore * finalConfig.jaroWinklerWeight + ngramScore * finalConfig.ngramWeight;
|
|
316
|
+
return {
|
|
317
|
+
similarity,
|
|
318
|
+
isMatch: similarity >= finalConfig.threshold,
|
|
319
|
+
scores: {
|
|
320
|
+
levenshtein: levenshteinScore,
|
|
321
|
+
jaroWinkler: jaroWinklerScore,
|
|
322
|
+
ngram: ngramScore
|
|
323
|
+
},
|
|
324
|
+
normalizedSource,
|
|
325
|
+
normalizedTarget
|
|
326
|
+
};
|
|
327
|
+
}
|
|
328
|
+
function tokenize(s) {
|
|
329
|
+
return s.replace(/([a-z])([A-Z])/g, "$1 $2").replace(/[_-]/g, " ").replace(/\s+/g, " ").trim().toLowerCase().split(" ").filter((token) => token.length > 0);
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
// src/ai/alias-generator.ts
|
|
333
|
+
var DEFAULT_ALIAS_CONFIG = {
|
|
334
|
+
includeText: true,
|
|
335
|
+
includeAriaLabel: true,
|
|
336
|
+
includePlaceholder: true,
|
|
337
|
+
includeTitle: true,
|
|
338
|
+
includeSynonyms: true,
|
|
339
|
+
maxAliases: 20,
|
|
340
|
+
minLength: 2,
|
|
341
|
+
maxLength: 50
|
|
342
|
+
};
|
|
343
|
+
var SYNONYMS = {
|
|
344
|
+
// Submit-related
|
|
345
|
+
submit: ["send", "go", "confirm", "ok", "apply", "save", "done", "finish"],
|
|
346
|
+
send: ["submit", "deliver", "post"],
|
|
347
|
+
save: ["submit", "store", "keep", "apply"],
|
|
348
|
+
cancel: ["close", "dismiss", "abort", "back", "exit", "quit", "nevermind"],
|
|
349
|
+
close: ["cancel", "dismiss", "exit", "x"],
|
|
350
|
+
delete: ["remove", "trash", "erase", "clear", "destroy"],
|
|
351
|
+
remove: ["delete", "clear", "discard"],
|
|
352
|
+
edit: ["modify", "change", "update", "alter"],
|
|
353
|
+
update: ["edit", "modify", "save", "refresh"],
|
|
354
|
+
add: ["create", "new", "plus", "insert"],
|
|
355
|
+
create: ["add", "new", "make"],
|
|
356
|
+
search: ["find", "lookup", "query", "filter"],
|
|
357
|
+
find: ["search", "locate", "lookup"],
|
|
358
|
+
login: ["signin", "sign in", "log in", "authenticate", "enter"],
|
|
359
|
+
logout: ["signout", "sign out", "log out", "exit"],
|
|
360
|
+
register: ["signup", "sign up", "join", "create account"],
|
|
361
|
+
next: ["continue", "forward", "proceed", "advance"],
|
|
362
|
+
previous: ["back", "backward", "return", "prior"],
|
|
363
|
+
back: ["previous", "return", "backward"],
|
|
364
|
+
start: ["begin", "launch", "initiate", "run", "execute"],
|
|
365
|
+
stop: ["end", "halt", "pause", "terminate"],
|
|
366
|
+
enable: ["activate", "turn on", "switch on"],
|
|
367
|
+
disable: ["deactivate", "turn off", "switch off"],
|
|
368
|
+
show: ["display", "reveal", "view", "open"],
|
|
369
|
+
hide: ["conceal", "collapse", "close"],
|
|
370
|
+
expand: ["open", "show", "unfold", "reveal"],
|
|
371
|
+
collapse: ["close", "hide", "fold", "minimize"],
|
|
372
|
+
yes: ["ok", "confirm", "agree", "accept"],
|
|
373
|
+
no: ["cancel", "decline", "reject", "deny"],
|
|
374
|
+
help: ["support", "assistance", "info", "information", "faq"],
|
|
375
|
+
settings: ["preferences", "options", "config", "configuration"],
|
|
376
|
+
profile: ["account", "user", "me"],
|
|
377
|
+
download: ["export", "save", "get"],
|
|
378
|
+
upload: ["import", "load", "attach"],
|
|
379
|
+
refresh: ["reload", "update", "sync"],
|
|
380
|
+
copy: ["duplicate", "clone"],
|
|
381
|
+
paste: ["insert"],
|
|
382
|
+
select: ["choose", "pick"],
|
|
383
|
+
toggle: ["switch", "flip"],
|
|
384
|
+
// Form fields
|
|
385
|
+
email: ["e-mail", "mail"],
|
|
386
|
+
password: ["pass", "pwd", "secret"],
|
|
387
|
+
username: ["user", "login", "account", "name"],
|
|
388
|
+
firstname: ["first name", "given name", "forename"],
|
|
389
|
+
lastname: ["last name", "surname", "family name"],
|
|
390
|
+
fullname: ["full name", "name", "complete name"],
|
|
391
|
+
phone: ["telephone", "tel", "mobile", "cell"],
|
|
392
|
+
address: ["location", "street"],
|
|
393
|
+
city: ["town"],
|
|
394
|
+
country: ["nation"],
|
|
395
|
+
zip: ["zipcode", "postal", "postal code", "postcode"],
|
|
396
|
+
// Navigation
|
|
397
|
+
home: ["main", "start", "dashboard"],
|
|
398
|
+
menu: ["navigation", "nav"],
|
|
399
|
+
sidebar: ["side bar", "side panel", "side menu"]
|
|
400
|
+
};
|
|
401
|
+
var ELEMENT_ACTION_WORDS = {
|
|
402
|
+
button: ["button", "btn", "click"],
|
|
403
|
+
input: ["input", "field", "textbox", "box"],
|
|
404
|
+
textarea: ["textarea", "text area", "text field", "multiline"],
|
|
405
|
+
select: ["select", "dropdown", "combo", "picker", "chooser"],
|
|
406
|
+
checkbox: ["checkbox", "check", "tick"],
|
|
407
|
+
radio: ["radio", "option", "choice"],
|
|
408
|
+
link: ["link", "anchor", "href"],
|
|
409
|
+
form: ["form"],
|
|
410
|
+
menu: ["menu"],
|
|
411
|
+
menuitem: ["menu item", "option"],
|
|
412
|
+
tab: ["tab"],
|
|
413
|
+
dialog: ["dialog", "modal", "popup"],
|
|
414
|
+
switch: ["switch", "toggle"],
|
|
415
|
+
slider: ["slider", "range"]
|
|
416
|
+
};
|
|
417
|
+
function normalizeAlias(text) {
|
|
418
|
+
return text.toLowerCase().replace(/[^\w\s]/g, " ").replace(/\s+/g, " ").trim();
|
|
419
|
+
}
|
|
420
|
+
function extractWords(text) {
|
|
421
|
+
const tokens = tokenize(text);
|
|
422
|
+
return tokens.filter((t) => t.length >= 2);
|
|
423
|
+
}
|
|
424
|
+
function generateTextAliases(text, config) {
|
|
425
|
+
if (!text || !config.includeText) return [];
|
|
426
|
+
const aliases = [];
|
|
427
|
+
const normalized = normalizeAlias(text);
|
|
428
|
+
if (normalized.length >= config.minLength && normalized.length <= config.maxLength) {
|
|
429
|
+
aliases.push(normalized);
|
|
430
|
+
}
|
|
431
|
+
const words = extractWords(text);
|
|
432
|
+
for (const word of words) {
|
|
433
|
+
if (word.length >= config.minLength) {
|
|
434
|
+
aliases.push(word);
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
if (words.length >= 2 && words.length <= 4) {
|
|
438
|
+
const twoWords = words.slice(0, 2).join(" ");
|
|
439
|
+
if (twoWords.length <= config.maxLength) {
|
|
440
|
+
aliases.push(twoWords);
|
|
441
|
+
}
|
|
442
|
+
if (words.length > 2) {
|
|
443
|
+
const lastTwo = words.slice(-2).join(" ");
|
|
444
|
+
if (lastTwo.length <= config.maxLength) {
|
|
445
|
+
aliases.push(lastTwo);
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
return aliases;
|
|
450
|
+
}
|
|
451
|
+
function generateSynonyms(aliases, config) {
|
|
452
|
+
if (!config.includeSynonyms) return [];
|
|
453
|
+
const synonyms = [];
|
|
454
|
+
for (const alias of aliases) {
|
|
455
|
+
const words = alias.toLowerCase().split(/\s+/);
|
|
456
|
+
for (const word of words) {
|
|
457
|
+
if (SYNONYMS[word]) {
|
|
458
|
+
for (const synonym of SYNONYMS[word]) {
|
|
459
|
+
const newAlias = alias.toLowerCase().replace(word, synonym);
|
|
460
|
+
if (newAlias !== alias.toLowerCase()) {
|
|
461
|
+
synonyms.push(newAlias);
|
|
462
|
+
}
|
|
463
|
+
if (synonym.length >= config.minLength) {
|
|
464
|
+
synonyms.push(synonym);
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
return synonyms;
|
|
471
|
+
}
|
|
472
|
+
function generateTypeAliases(elementType) {
|
|
473
|
+
const type = elementType.toLowerCase();
|
|
474
|
+
return ELEMENT_ACTION_WORDS[type] || [type];
|
|
475
|
+
}
|
|
476
|
+
function generateAliases(input, config = {}) {
|
|
477
|
+
const finalConfig = { ...DEFAULT_ALIAS_CONFIG, ...config };
|
|
478
|
+
const aliasSet = /* @__PURE__ */ new Set();
|
|
479
|
+
const addAlias = (alias) => {
|
|
480
|
+
const normalized = normalizeAlias(alias);
|
|
481
|
+
if (normalized.length >= finalConfig.minLength && normalized.length <= finalConfig.maxLength) {
|
|
482
|
+
aliasSet.add(normalized);
|
|
483
|
+
}
|
|
484
|
+
};
|
|
485
|
+
const addAliases = (aliases2) => {
|
|
486
|
+
for (const alias of aliases2) {
|
|
487
|
+
addAlias(alias);
|
|
488
|
+
}
|
|
489
|
+
};
|
|
490
|
+
if (finalConfig.includeText && input.textContent) {
|
|
491
|
+
addAliases(generateTextAliases(input.textContent, finalConfig));
|
|
492
|
+
}
|
|
493
|
+
if (finalConfig.includeAriaLabel && input.ariaLabel) {
|
|
494
|
+
addAliases(generateTextAliases(input.ariaLabel, finalConfig));
|
|
495
|
+
}
|
|
496
|
+
if (finalConfig.includeAriaLabel && input.ariaLabelledBy) {
|
|
497
|
+
addAliases(generateTextAliases(input.ariaLabelledBy, finalConfig));
|
|
498
|
+
}
|
|
499
|
+
if (finalConfig.includePlaceholder && input.placeholder) {
|
|
500
|
+
addAliases(generateTextAliases(input.placeholder, finalConfig));
|
|
501
|
+
}
|
|
502
|
+
if (finalConfig.includeTitle && input.title) {
|
|
503
|
+
addAliases(generateTextAliases(input.title, finalConfig));
|
|
504
|
+
}
|
|
505
|
+
if (input.labelText) {
|
|
506
|
+
addAliases(generateTextAliases(input.labelText, finalConfig));
|
|
507
|
+
}
|
|
508
|
+
if (input.id) {
|
|
509
|
+
addAliases(extractWords(input.id));
|
|
510
|
+
}
|
|
511
|
+
if (input.name) {
|
|
512
|
+
addAliases(extractWords(input.name));
|
|
513
|
+
}
|
|
514
|
+
if (input.value && (input.elementType === "button" || input.inputType === "submit" || input.inputType === "button")) {
|
|
515
|
+
addAliases(generateTextAliases(input.value, finalConfig));
|
|
516
|
+
}
|
|
517
|
+
if (input.elementType) {
|
|
518
|
+
addAliases(generateTypeAliases(input.elementType));
|
|
519
|
+
}
|
|
520
|
+
if (input.inputType) {
|
|
521
|
+
addAlias(input.inputType);
|
|
522
|
+
if (input.inputType === "email") {
|
|
523
|
+
addAliases(["email", "e-mail", "email address"]);
|
|
524
|
+
} else if (input.inputType === "password") {
|
|
525
|
+
addAliases(["password", "pass", "pwd"]);
|
|
526
|
+
} else if (input.inputType === "tel") {
|
|
527
|
+
addAliases(["phone", "telephone", "mobile"]);
|
|
528
|
+
} else if (input.inputType === "url") {
|
|
529
|
+
addAliases(["url", "website", "link", "address"]);
|
|
530
|
+
} else if (input.inputType === "search") {
|
|
531
|
+
addAliases(["search", "find", "query"]);
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
if (finalConfig.includeSynonyms) {
|
|
535
|
+
const currentAliases = Array.from(aliasSet);
|
|
536
|
+
addAliases(generateSynonyms(currentAliases, finalConfig));
|
|
537
|
+
}
|
|
538
|
+
let aliases = Array.from(aliasSet);
|
|
539
|
+
aliases.sort((a, b) => a.length - b.length);
|
|
540
|
+
if (aliases.length > finalConfig.maxAliases) {
|
|
541
|
+
aliases = aliases.slice(0, finalConfig.maxAliases);
|
|
542
|
+
}
|
|
543
|
+
return aliases;
|
|
544
|
+
}
|
|
545
|
+
function generateDescription(input) {
|
|
546
|
+
const parts = [];
|
|
547
|
+
let name = input.ariaLabel || input.labelText || input.textContent || input.placeholder || input.title || input.id || input.name;
|
|
548
|
+
if (name) {
|
|
549
|
+
name = name.trim();
|
|
550
|
+
if (name.length > 30) {
|
|
551
|
+
name = name.substring(0, 27) + "...";
|
|
552
|
+
}
|
|
553
|
+
parts.push(`"${name}"`);
|
|
554
|
+
}
|
|
555
|
+
const typeWords = ELEMENT_ACTION_WORDS[input.elementType || ""] || [input.elementType || "element"];
|
|
556
|
+
parts.push(typeWords[0]);
|
|
557
|
+
if (input.inputType && input.inputType !== "text") {
|
|
558
|
+
parts.push(`(${input.inputType})`);
|
|
559
|
+
}
|
|
560
|
+
return parts.join(" ");
|
|
561
|
+
}
|
|
562
|
+
|
|
193
563
|
// src/core/registry.ts
|
|
194
564
|
function getElementState(element) {
|
|
195
565
|
const rect = element.getBoundingClientRect();
|
|
@@ -388,9 +758,13 @@ var UIBridgeRegistry = class {
|
|
|
388
758
|
registerElement(id, element, options = {}) {
|
|
389
759
|
const type = options.type ?? inferElementType(element);
|
|
390
760
|
const actions = options.actions ?? inferActions(type);
|
|
391
|
-
element.
|
|
761
|
+
const existingUiId = element.getAttribute("data-ui-id");
|
|
762
|
+
const actualId = existingUiId || id;
|
|
763
|
+
if (!existingUiId) {
|
|
764
|
+
element.setAttribute("data-ui-id", actualId);
|
|
765
|
+
}
|
|
392
766
|
const registered = {
|
|
393
|
-
id,
|
|
767
|
+
id: actualId,
|
|
394
768
|
element,
|
|
395
769
|
type,
|
|
396
770
|
label: options.label,
|
|
@@ -401,8 +775,8 @@ var UIBridgeRegistry = class {
|
|
|
401
775
|
registeredAt: Date.now(),
|
|
402
776
|
mounted: true
|
|
403
777
|
};
|
|
404
|
-
this.elements.set(
|
|
405
|
-
this.emit("element:registered", { id, type, label: options.label });
|
|
778
|
+
this.elements.set(actualId, registered);
|
|
779
|
+
this.emit("element:registered", { id: actualId, type, label: options.label });
|
|
406
780
|
return registered;
|
|
407
781
|
}
|
|
408
782
|
/**
|
|
@@ -442,6 +816,209 @@ var UIBridgeRegistry = class {
|
|
|
442
816
|
}
|
|
443
817
|
return void 0;
|
|
444
818
|
}
|
|
819
|
+
/**
|
|
820
|
+
* Search for elements using AI search criteria
|
|
821
|
+
*/
|
|
822
|
+
searchElements(criteria) {
|
|
823
|
+
const results = [];
|
|
824
|
+
const threshold = criteria.fuzzyThreshold ?? 0.7;
|
|
825
|
+
for (const element of this.elements.values()) {
|
|
826
|
+
if (!element.mounted) continue;
|
|
827
|
+
const state = element.getState();
|
|
828
|
+
if (!criteria.fuzzy && !state.visible) continue;
|
|
829
|
+
const aliases = element.aliases ?? this.generateElementAliases(element);
|
|
830
|
+
const textContent = state.textContent?.trim() || "";
|
|
831
|
+
const label = element.label || "";
|
|
832
|
+
let maxScore = 0;
|
|
833
|
+
const matchReasons = [];
|
|
834
|
+
const scores = {};
|
|
835
|
+
if (criteria.text) {
|
|
836
|
+
if (textContent.toLowerCase() === criteria.text.toLowerCase() || label.toLowerCase() === criteria.text.toLowerCase()) {
|
|
837
|
+
maxScore = 1;
|
|
838
|
+
matchReasons.push("exact text match");
|
|
839
|
+
scores.text = 1;
|
|
840
|
+
} else if (criteria.fuzzy !== false) {
|
|
841
|
+
const textResult = fuzzyMatch(criteria.text, textContent, { threshold });
|
|
842
|
+
const labelResult = fuzzyMatch(criteria.text, label, { threshold });
|
|
843
|
+
const bestResult = textResult.similarity > labelResult.similarity ? textResult : labelResult;
|
|
844
|
+
if (bestResult.isMatch) {
|
|
845
|
+
scores.text = bestResult.similarity;
|
|
846
|
+
if (bestResult.similarity > maxScore) {
|
|
847
|
+
maxScore = bestResult.similarity;
|
|
848
|
+
matchReasons.push(`text similarity: ${(bestResult.similarity * 100).toFixed(0)}%`);
|
|
849
|
+
}
|
|
850
|
+
}
|
|
851
|
+
}
|
|
852
|
+
}
|
|
853
|
+
if (criteria.textContains) {
|
|
854
|
+
if (textContent.toLowerCase().includes(criteria.textContains.toLowerCase()) || label.toLowerCase().includes(criteria.textContains.toLowerCase())) {
|
|
855
|
+
const containsScore = 0.85;
|
|
856
|
+
scores.text = Math.max(scores.text ?? 0, containsScore);
|
|
857
|
+
if (containsScore > maxScore) {
|
|
858
|
+
maxScore = containsScore;
|
|
859
|
+
matchReasons.push("text contains");
|
|
860
|
+
}
|
|
861
|
+
}
|
|
862
|
+
}
|
|
863
|
+
if (criteria.accessibleName) {
|
|
864
|
+
const ariaLabel = element.element.getAttribute("aria-label") || "";
|
|
865
|
+
const accessibleName = ariaLabel || label || textContent;
|
|
866
|
+
if (accessibleName.toLowerCase() === criteria.accessibleName.toLowerCase()) {
|
|
867
|
+
scores.accessibility = 1;
|
|
868
|
+
if (1 > maxScore) {
|
|
869
|
+
maxScore = 1;
|
|
870
|
+
matchReasons.push("accessible name match");
|
|
871
|
+
}
|
|
872
|
+
} else if (criteria.fuzzy !== false) {
|
|
873
|
+
const result = fuzzyMatch(criteria.accessibleName, accessibleName, { threshold });
|
|
874
|
+
if (result.isMatch) {
|
|
875
|
+
scores.accessibility = result.similarity;
|
|
876
|
+
if (result.similarity > maxScore) {
|
|
877
|
+
maxScore = result.similarity;
|
|
878
|
+
matchReasons.push(`accessible name similarity: ${(result.similarity * 100).toFixed(0)}%`);
|
|
879
|
+
}
|
|
880
|
+
}
|
|
881
|
+
}
|
|
882
|
+
}
|
|
883
|
+
if (criteria.role) {
|
|
884
|
+
const role = element.element.getAttribute("role") || this.inferRole(element.type);
|
|
885
|
+
if (role?.toLowerCase() === criteria.role.toLowerCase()) {
|
|
886
|
+
scores.role = 1;
|
|
887
|
+
if (1 > maxScore) {
|
|
888
|
+
maxScore = 1;
|
|
889
|
+
matchReasons.push(`role: ${criteria.role}`);
|
|
890
|
+
}
|
|
891
|
+
}
|
|
892
|
+
}
|
|
893
|
+
if (criteria.type) {
|
|
894
|
+
if (element.type === criteria.type) {
|
|
895
|
+
const typeScore = 0.9;
|
|
896
|
+
scores.role = Math.max(scores.role ?? 0, typeScore);
|
|
897
|
+
if (typeScore > maxScore) {
|
|
898
|
+
maxScore = typeScore;
|
|
899
|
+
matchReasons.push(`type: ${criteria.type}`);
|
|
900
|
+
}
|
|
901
|
+
}
|
|
902
|
+
}
|
|
903
|
+
for (const alias of aliases) {
|
|
904
|
+
const searchText = criteria.text || criteria.textContains || criteria.accessibleName;
|
|
905
|
+
if (searchText) {
|
|
906
|
+
if (alias.toLowerCase() === searchText.toLowerCase()) {
|
|
907
|
+
scores.fuzzy = 1;
|
|
908
|
+
if (1 > maxScore) {
|
|
909
|
+
maxScore = 1;
|
|
910
|
+
matchReasons.push(`alias: "${alias}"`);
|
|
911
|
+
}
|
|
912
|
+
} else if (criteria.fuzzy !== false) {
|
|
913
|
+
const result = fuzzyMatch(searchText, alias, { threshold });
|
|
914
|
+
if (result.isMatch && result.similarity > (scores.fuzzy ?? 0)) {
|
|
915
|
+
scores.fuzzy = result.similarity;
|
|
916
|
+
if (result.similarity > maxScore) {
|
|
917
|
+
maxScore = result.similarity;
|
|
918
|
+
matchReasons.push(`fuzzy alias: "${alias}"`);
|
|
919
|
+
}
|
|
920
|
+
}
|
|
921
|
+
}
|
|
922
|
+
}
|
|
923
|
+
}
|
|
924
|
+
if (maxScore >= threshold) {
|
|
925
|
+
const aiElement = {
|
|
926
|
+
id: element.id,
|
|
927
|
+
type: element.type,
|
|
928
|
+
label: element.label,
|
|
929
|
+
tagName: element.element.tagName.toLowerCase(),
|
|
930
|
+
role: element.element.getAttribute("role") || void 0,
|
|
931
|
+
accessibleName: element.element.getAttribute("aria-label") || element.label,
|
|
932
|
+
actions: element.actions,
|
|
933
|
+
state,
|
|
934
|
+
registered: true,
|
|
935
|
+
description: element.description || generateDescription({
|
|
936
|
+
textContent,
|
|
937
|
+
ariaLabel: element.element.getAttribute("aria-label"),
|
|
938
|
+
elementType: element.type,
|
|
939
|
+
id: element.id,
|
|
940
|
+
labelText: element.label
|
|
941
|
+
}),
|
|
942
|
+
aliases,
|
|
943
|
+
purpose: element.purpose,
|
|
944
|
+
suggestedActions: [],
|
|
945
|
+
semanticType: element.semanticType
|
|
946
|
+
};
|
|
947
|
+
results.push({
|
|
948
|
+
element: aiElement,
|
|
949
|
+
confidence: maxScore,
|
|
950
|
+
matchReasons,
|
|
951
|
+
scores
|
|
952
|
+
});
|
|
953
|
+
}
|
|
954
|
+
}
|
|
955
|
+
results.sort((a, b) => b.confidence - a.confidence);
|
|
956
|
+
return results;
|
|
957
|
+
}
|
|
958
|
+
/**
|
|
959
|
+
* Find element by visible text
|
|
960
|
+
*/
|
|
961
|
+
findByText(text, fuzzy = true) {
|
|
962
|
+
const results = this.searchElements({ text, fuzzy, fuzzyThreshold: fuzzy ? 0.7 : 1 });
|
|
963
|
+
if (results.length > 0) {
|
|
964
|
+
return this.elements.get(results[0].element.id);
|
|
965
|
+
}
|
|
966
|
+
return void 0;
|
|
967
|
+
}
|
|
968
|
+
/**
|
|
969
|
+
* Find element by accessible name
|
|
970
|
+
*/
|
|
971
|
+
findByAccessibleName(name) {
|
|
972
|
+
const results = this.searchElements({ accessibleName: name, fuzzy: true });
|
|
973
|
+
if (results.length > 0) {
|
|
974
|
+
return this.elements.get(results[0].element.id);
|
|
975
|
+
}
|
|
976
|
+
return void 0;
|
|
977
|
+
}
|
|
978
|
+
/**
|
|
979
|
+
* Generate aliases for an element
|
|
980
|
+
*/
|
|
981
|
+
generateElementAliases(element) {
|
|
982
|
+
const state = element.getState();
|
|
983
|
+
return generateAliases({
|
|
984
|
+
textContent: state.textContent,
|
|
985
|
+
ariaLabel: element.element.getAttribute("aria-label"),
|
|
986
|
+
placeholder: element.element.getAttribute("placeholder"),
|
|
987
|
+
title: element.element.getAttribute("title"),
|
|
988
|
+
elementType: element.type,
|
|
989
|
+
tagName: element.element.tagName.toLowerCase(),
|
|
990
|
+
id: element.id,
|
|
991
|
+
labelText: element.label
|
|
992
|
+
});
|
|
993
|
+
}
|
|
994
|
+
/**
|
|
995
|
+
* Infer ARIA role from element type
|
|
996
|
+
*/
|
|
997
|
+
inferRole(type) {
|
|
998
|
+
const roleMap = {
|
|
999
|
+
button: "button",
|
|
1000
|
+
input: "textbox",
|
|
1001
|
+
select: "combobox",
|
|
1002
|
+
checkbox: "checkbox",
|
|
1003
|
+
radio: "radio",
|
|
1004
|
+
link: "link",
|
|
1005
|
+
form: void 0,
|
|
1006
|
+
textarea: "textbox",
|
|
1007
|
+
menu: "menu",
|
|
1008
|
+
menuitem: "menuitem",
|
|
1009
|
+
tab: "tab",
|
|
1010
|
+
dialog: "dialog",
|
|
1011
|
+
custom: void 0,
|
|
1012
|
+
switch: "switch",
|
|
1013
|
+
slider: "slider",
|
|
1014
|
+
combobox: "combobox",
|
|
1015
|
+
listbox: "listbox",
|
|
1016
|
+
option: "option",
|
|
1017
|
+
textbox: "textbox",
|
|
1018
|
+
generic: void 0
|
|
1019
|
+
};
|
|
1020
|
+
return roleMap[type];
|
|
1021
|
+
}
|
|
445
1022
|
/**
|
|
446
1023
|
* Register a component
|
|
447
1024
|
*/
|
|
@@ -4136,24 +4713,24 @@ function useAutoRegister(options = {}) {
|
|
|
4136
4713
|
const type = inferElementType2(element);
|
|
4137
4714
|
const actions = inferActions2(type);
|
|
4138
4715
|
const label = getAccessibleLabel(element);
|
|
4139
|
-
bridge.registry.registerElement(uniqueId, element, {
|
|
4716
|
+
const registered = bridge.registry.registerElement(uniqueId, element, {
|
|
4140
4717
|
type,
|
|
4141
4718
|
actions,
|
|
4142
4719
|
label
|
|
4143
4720
|
});
|
|
4144
|
-
registeredElementsRef.current.set(element,
|
|
4145
|
-
onRegister?.(
|
|
4721
|
+
registeredElementsRef.current.set(element, registered.id);
|
|
4722
|
+
onRegister?.(registered.id, element);
|
|
4146
4723
|
} else {
|
|
4147
4724
|
const type = inferElementType2(element);
|
|
4148
4725
|
const actions = inferActions2(type);
|
|
4149
4726
|
const label = getAccessibleLabel(element);
|
|
4150
|
-
bridge.registry.registerElement(id, element, {
|
|
4727
|
+
const registered = bridge.registry.registerElement(id, element, {
|
|
4151
4728
|
type,
|
|
4152
4729
|
actions,
|
|
4153
4730
|
label
|
|
4154
4731
|
});
|
|
4155
|
-
registeredElementsRef.current.set(element, id);
|
|
4156
|
-
onRegister?.(id, element);
|
|
4732
|
+
registeredElementsRef.current.set(element, registered.id);
|
|
4733
|
+
onRegister?.(registered.id, element);
|
|
4157
4734
|
}
|
|
4158
4735
|
},
|
|
4159
4736
|
[bridge, idStrategy, customGenerateId, onRegister]
|