@qontinui/ui-bridge 0.1.1 → 0.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.
Files changed (112) hide show
  1. package/dist/ai/index.d.mts +893 -0
  2. package/dist/ai/index.d.ts +893 -0
  3. package/dist/ai/index.js +3897 -0
  4. package/dist/ai/index.js.map +1 -0
  5. package/dist/ai/index.mjs +3839 -0
  6. package/dist/ai/index.mjs.map +1 -0
  7. package/dist/babel-plugin/index.js +515 -0
  8. package/dist/babel-plugin/index.js.map +1 -0
  9. package/dist/babel-plugin/index.mjs +499 -0
  10. package/dist/babel-plugin/index.mjs.map +1 -0
  11. package/dist/control/index.d.mts +5 -4
  12. package/dist/control/index.d.ts +5 -4
  13. package/dist/core/index.d.mts +115 -42
  14. package/dist/core/index.d.ts +115 -42
  15. package/dist/core/index.js +0 -983
  16. package/dist/core/index.js.map +1 -1
  17. package/dist/core/index.mjs +1 -972
  18. package/dist/core/index.mjs.map +1 -1
  19. package/dist/debug/index.d.mts +3 -3
  20. package/dist/debug/index.d.ts +3 -3
  21. package/dist/index.d.mts +8 -7
  22. package/dist/index.d.ts +8 -7
  23. package/dist/index.js +8249 -4163
  24. package/dist/index.js.map +1 -1
  25. package/dist/index.mjs +8193 -4152
  26. package/dist/index.mjs.map +1 -1
  27. package/dist/{metrics-QCnK0EFw.d.ts → metrics-BfiT_rhZ.d.ts} +2 -2
  28. package/dist/{metrics-BCG7z7Aq.d.mts → metrics-DTA2bwG7.d.mts} +2 -2
  29. package/dist/native/control/index.js +453 -0
  30. package/dist/native/control/index.js.map +1 -0
  31. package/dist/native/control/index.mjs +450 -0
  32. package/dist/native/control/index.mjs.map +1 -0
  33. package/dist/native/core/index.js +486 -0
  34. package/dist/native/core/index.js.map +1 -0
  35. package/dist/native/core/index.mjs +475 -0
  36. package/dist/native/core/index.mjs.map +1 -0
  37. package/dist/native/debug/index.js +451 -0
  38. package/dist/native/debug/index.js.map +1 -0
  39. package/dist/native/debug/index.mjs +449 -0
  40. package/dist/native/debug/index.mjs.map +1 -0
  41. package/dist/native/index.js +2274 -0
  42. package/dist/native/index.js.map +1 -0
  43. package/dist/native/index.mjs +2246 -0
  44. package/dist/native/index.mjs.map +1 -0
  45. package/dist/native/react/index.js +1401 -0
  46. package/dist/native/react/index.js.map +1 -0
  47. package/dist/native/react/index.mjs +1389 -0
  48. package/dist/native/react/index.mjs.map +1 -0
  49. package/dist/native/server/index.js +415 -0
  50. package/dist/native/server/index.js.map +1 -0
  51. package/dist/native/server/index.mjs +410 -0
  52. package/dist/native/server/index.mjs.map +1 -0
  53. package/dist/react/index.d.mts +20 -6
  54. package/dist/react/index.d.ts +20 -6
  55. package/dist/react/index.js +629 -14
  56. package/dist/react/index.js.map +1 -1
  57. package/dist/react/index.mjs +629 -14
  58. package/dist/react/index.mjs.map +1 -1
  59. package/dist/{registry-CT6BVVKr.d.mts → registry-BKLEm-yk.d.ts} +29 -14
  60. package/dist/{registry-D4mQ01B3.d.ts → registry-BmZgyCz8.d.mts} +29 -14
  61. package/dist/render-log/index.d.mts +1 -1
  62. package/dist/render-log/index.d.ts +1 -1
  63. package/dist/server/express.d.mts +36 -0
  64. package/dist/server/express.d.ts +36 -0
  65. package/dist/server/express.js +196 -0
  66. package/dist/server/express.js.map +1 -0
  67. package/dist/server/express.mjs +192 -0
  68. package/dist/server/express.mjs.map +1 -0
  69. package/dist/server/handlers.d.mts +93 -0
  70. package/dist/server/handlers.d.ts +93 -0
  71. package/dist/server/handlers.js +4278 -0
  72. package/dist/server/handlers.js.map +1 -0
  73. package/dist/server/handlers.mjs +4275 -0
  74. package/dist/server/handlers.mjs.map +1 -0
  75. package/dist/server/index.d.mts +10 -0
  76. package/dist/server/index.d.ts +10 -0
  77. package/dist/server/index.js +5352 -0
  78. package/dist/server/index.js.map +1 -0
  79. package/dist/server/index.mjs +5337 -0
  80. package/dist/server/index.mjs.map +1 -0
  81. package/dist/server/nextjs.d.mts +126 -0
  82. package/dist/server/nextjs.d.ts +126 -0
  83. package/dist/server/nextjs.js +287 -0
  84. package/dist/server/nextjs.js.map +1 -0
  85. package/dist/server/nextjs.mjs +282 -0
  86. package/dist/server/nextjs.mjs.map +1 -0
  87. package/dist/server/standalone.d.mts +6 -0
  88. package/dist/server/standalone.d.ts +6 -0
  89. package/dist/server/standalone.js +719 -0
  90. package/dist/server/standalone.js.map +1 -0
  91. package/dist/server/standalone.mjs +715 -0
  92. package/dist/server/standalone.mjs.map +1 -0
  93. package/dist/standalone-BURj8J3G.d.ts +212 -0
  94. package/dist/standalone-Dwmel29d.d.mts +212 -0
  95. package/dist/swc-plugin/index.d.mts +79 -0
  96. package/dist/swc-plugin/index.d.ts +79 -0
  97. package/dist/swc-plugin/index.js +15 -0
  98. package/dist/swc-plugin/index.js.map +1 -0
  99. package/dist/swc-plugin/index.mjs +9 -0
  100. package/dist/swc-plugin/index.mjs.map +1 -0
  101. package/dist/types-B5Q0GVo0.d.mts +646 -0
  102. package/dist/{types-DdJD9yw5.d.mts → types-B7J7noLK.d.mts} +1 -1
  103. package/dist/{types-BDkXy5si.d.ts → types-BkNRILUa.d.ts} +1 -1
  104. package/dist/types-CEQLnFMv.d.mts +156 -0
  105. package/dist/types-CHnlwiTK.d.ts +156 -0
  106. package/dist/types-DfPqwU-i.d.ts +646 -0
  107. package/dist/{types-BpvpStn3.d.mts → types-jKVgTI6_.d.mts} +364 -160
  108. package/dist/{types-BpvpStn3.d.ts → types-jKVgTI6_.d.ts} +364 -160
  109. package/package.json +111 -3
  110. package/swc-plugin-wasm/ui_bridge_swc_plugin.wasm +0 -0
  111. package/dist/websocket-client-B2LC9CYc.d.mts +0 -124
  112. package/dist/websocket-client-DupH0X7B.d.ts +0 -124
@@ -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.setAttribute("data-ui-id", id);
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(id, registered);
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
  */
@@ -458,7 +1035,9 @@ var UIBridgeRegistry = class {
458
1035
  })) ?? [],
459
1036
  elementIds: options.elementIds,
460
1037
  registeredAt: Date.now(),
461
- mounted: true
1038
+ mounted: true,
1039
+ getState: options.getState,
1040
+ getComputed: options.getComputed
462
1041
  };
463
1042
  this.components.set(id, registered);
464
1043
  this.emit("component:registered", { id, name: options.name });
@@ -489,6 +1068,20 @@ var UIBridgeRegistry = class {
489
1068
  getAllComponents() {
490
1069
  return Array.from(this.components.values());
491
1070
  }
1071
+ /**
1072
+ * Get the current state and computed properties of a component
1073
+ */
1074
+ getComponentState(id) {
1075
+ const component = this.components.get(id);
1076
+ if (!component || !component.mounted) {
1077
+ return null;
1078
+ }
1079
+ return {
1080
+ state: component.getState?.() ?? {},
1081
+ computed: component.getComputed?.() ?? {},
1082
+ timestamp: Date.now()
1083
+ };
1084
+ }
492
1085
  /**
493
1086
  * Register a workflow
494
1087
  */
@@ -3261,11 +3854,31 @@ function useUIComponent(options) {
3261
3854
  const registeredRef = react.useRef(false);
3262
3855
  const actionsRef = react.useRef(options.actions || []);
3263
3856
  const elementIdsRef = react.useRef(options.elementIds || []);
3857
+ const stateRef = react.useRef(options.state);
3858
+ const computedRef = react.useRef(options.computed);
3264
3859
  const { id, name, description, autoRegister = true } = options;
3265
3860
  react.useEffect(() => {
3266
3861
  actionsRef.current = options.actions || [];
3267
3862
  elementIdsRef.current = options.elementIds || [];
3268
- }, [options.actions, options.elementIds]);
3863
+ stateRef.current = options.state;
3864
+ computedRef.current = options.computed;
3865
+ }, [options.actions, options.elementIds, options.state, options.computed]);
3866
+ const createGetComputed = react.useCallback(() => {
3867
+ return () => {
3868
+ const computed = computedRef.current;
3869
+ if (!computed) return {};
3870
+ const result = {};
3871
+ for (const [key, def] of Object.entries(computed)) {
3872
+ try {
3873
+ const getter = typeof def === "function" ? def : def.getter;
3874
+ result[key] = getter();
3875
+ } catch {
3876
+ result[key] = void 0;
3877
+ }
3878
+ }
3879
+ return result;
3880
+ };
3881
+ }, []);
3269
3882
  const register = react.useCallback(() => {
3270
3883
  if (!bridge || registeredRef.current) return;
3271
3884
  bridge.registry.registerComponent(id, {
@@ -3277,10 +3890,12 @@ function useUIComponent(options) {
3277
3890
  description: a.description,
3278
3891
  handler: a.handler
3279
3892
  })),
3280
- elementIds: elementIdsRef.current
3893
+ elementIds: elementIdsRef.current,
3894
+ getState: stateRef.current,
3895
+ getComputed: createGetComputed()
3281
3896
  });
3282
3897
  registeredRef.current = true;
3283
- }, [bridge, id, name, description]);
3898
+ }, [bridge, id, name, description, createGetComputed]);
3284
3899
  const unregister = react.useCallback(() => {
3285
3900
  if (!bridge || !registeredRef.current) return;
3286
3901
  bridge.registry.unregisterComponent(id);
@@ -4136,24 +4751,24 @@ function useAutoRegister(options = {}) {
4136
4751
  const type = inferElementType2(element);
4137
4752
  const actions = inferActions2(type);
4138
4753
  const label = getAccessibleLabel(element);
4139
- bridge.registry.registerElement(uniqueId, element, {
4754
+ const registered = bridge.registry.registerElement(uniqueId, element, {
4140
4755
  type,
4141
4756
  actions,
4142
4757
  label
4143
4758
  });
4144
- registeredElementsRef.current.set(element, uniqueId);
4145
- onRegister?.(uniqueId, element);
4759
+ registeredElementsRef.current.set(element, registered.id);
4760
+ onRegister?.(registered.id, element);
4146
4761
  } else {
4147
4762
  const type = inferElementType2(element);
4148
4763
  const actions = inferActions2(type);
4149
4764
  const label = getAccessibleLabel(element);
4150
- bridge.registry.registerElement(id, element, {
4765
+ const registered = bridge.registry.registerElement(id, element, {
4151
4766
  type,
4152
4767
  actions,
4153
4768
  label
4154
4769
  });
4155
- registeredElementsRef.current.set(element, id);
4156
- onRegister?.(id, element);
4770
+ registeredElementsRef.current.set(element, registered.id);
4771
+ onRegister?.(registered.id, element);
4157
4772
  }
4158
4773
  },
4159
4774
  [bridge, idStrategy, customGenerateId, onRegister]