@qontinui/ui-bridge 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. package/dist/control/index.d.mts +134 -0
  2. package/dist/control/index.d.ts +134 -0
  3. package/dist/control/index.js +924 -0
  4. package/dist/control/index.js.map +1 -0
  5. package/dist/control/index.mjs +919 -0
  6. package/dist/control/index.mjs.map +1 -0
  7. package/dist/core/index.d.mts +52 -0
  8. package/dist/core/index.d.ts +52 -0
  9. package/dist/core/index.js +1424 -0
  10. package/dist/core/index.js.map +1 -0
  11. package/dist/core/index.mjs +1409 -0
  12. package/dist/core/index.mjs.map +1 -0
  13. package/dist/debug/index.d.mts +93 -0
  14. package/dist/debug/index.d.ts +93 -0
  15. package/dist/debug/index.js +673 -0
  16. package/dist/debug/index.js.map +1 -0
  17. package/dist/debug/index.mjs +664 -0
  18. package/dist/debug/index.mjs.map +1 -0
  19. package/dist/index.d.mts +12 -0
  20. package/dist/index.d.ts +12 -0
  21. package/dist/index.js +4719 -0
  22. package/dist/index.js.map +1 -0
  23. package/dist/index.mjs +4665 -0
  24. package/dist/index.mjs.map +1 -0
  25. package/dist/metrics-BCG7z7Aq.d.mts +147 -0
  26. package/dist/metrics-QCnK0EFw.d.ts +147 -0
  27. package/dist/react/index.d.mts +786 -0
  28. package/dist/react/index.d.ts +786 -0
  29. package/dist/react/index.js +4312 -0
  30. package/dist/react/index.js.map +1 -0
  31. package/dist/react/index.mjs +4290 -0
  32. package/dist/react/index.mjs.map +1 -0
  33. package/dist/registry-CT6BVVKr.d.mts +253 -0
  34. package/dist/registry-D4mQ01B3.d.ts +253 -0
  35. package/dist/render-log/index.d.mts +340 -0
  36. package/dist/render-log/index.d.ts +340 -0
  37. package/dist/render-log/index.js +702 -0
  38. package/dist/render-log/index.js.map +1 -0
  39. package/dist/render-log/index.mjs +695 -0
  40. package/dist/render-log/index.mjs.map +1 -0
  41. package/dist/types-BDkXy5si.d.ts +354 -0
  42. package/dist/types-BpvpStn3.d.mts +802 -0
  43. package/dist/types-BpvpStn3.d.ts +802 -0
  44. package/dist/types-DdJD9yw5.d.mts +354 -0
  45. package/dist/websocket-client-B2LC9CYc.d.mts +124 -0
  46. package/dist/websocket-client-DupH0X7B.d.ts +124 -0
  47. package/package.json +83 -0
package/dist/index.js ADDED
@@ -0,0 +1,4719 @@
1
+ 'use strict';
2
+
3
+ var react = require('react');
4
+ var jsxRuntime = require('react/jsx-runtime');
5
+
6
+ // src/core/element-identifier.ts
7
+ var ID_ATTRIBUTES = ["data-ui-id", "data-testid", "data-awas-element", "id"];
8
+ function generateXPath(element) {
9
+ if (element.id) {
10
+ return `//*[@id="${element.id}"]`;
11
+ }
12
+ const parts = [];
13
+ let current = element;
14
+ while (current && current.nodeType === Node.ELEMENT_NODE) {
15
+ let selector = current.nodeName.toLowerCase();
16
+ const uiId = current.getAttribute("data-ui-id");
17
+ if (uiId) {
18
+ selector += `[@data-ui-id="${uiId}"]`;
19
+ parts.unshift(selector);
20
+ break;
21
+ }
22
+ const testId = current.getAttribute("data-testid");
23
+ if (testId) {
24
+ selector += `[@data-testid="${testId}"]`;
25
+ parts.unshift(selector);
26
+ break;
27
+ }
28
+ const id = current.id;
29
+ if (id) {
30
+ selector += `[@id="${id}"]`;
31
+ parts.unshift(selector);
32
+ break;
33
+ }
34
+ const parentEl = current.parentElement;
35
+ if (parentEl) {
36
+ const currentEl = current;
37
+ const siblings = Array.from(parentEl.children).filter(
38
+ (child) => child.nodeName === currentEl.nodeName
39
+ );
40
+ if (siblings.length > 1) {
41
+ const index = siblings.indexOf(currentEl) + 1;
42
+ selector += `[${index}]`;
43
+ }
44
+ }
45
+ parts.unshift(selector);
46
+ current = parentEl;
47
+ }
48
+ return "/" + parts.join("/");
49
+ }
50
+ function generateCSSSelector(element) {
51
+ const uiId = element.getAttribute("data-ui-id");
52
+ if (uiId) {
53
+ return `[data-ui-id="${uiId}"]`;
54
+ }
55
+ const testId = element.getAttribute("data-testid");
56
+ if (testId) {
57
+ return `[data-testid="${testId}"]`;
58
+ }
59
+ const awasId = element.getAttribute("data-awas-element");
60
+ if (awasId) {
61
+ return `[data-awas-element="${awasId}"]`;
62
+ }
63
+ if (element.id) {
64
+ return `#${CSS.escape(element.id)}`;
65
+ }
66
+ const path = [];
67
+ let current = element;
68
+ while (current && current.nodeType === Node.ELEMENT_NODE) {
69
+ let selector = current.nodeName.toLowerCase();
70
+ const parentUiId = current.getAttribute("data-ui-id");
71
+ if (parentUiId && current !== element) {
72
+ path.unshift(`[data-ui-id="${parentUiId}"]`);
73
+ break;
74
+ }
75
+ const parentTestId = current.getAttribute("data-testid");
76
+ if (parentTestId && current !== element) {
77
+ path.unshift(`[data-testid="${parentTestId}"]`);
78
+ break;
79
+ }
80
+ if (current.id) {
81
+ path.unshift(`#${CSS.escape(current.id)}`);
82
+ break;
83
+ }
84
+ const parentEl = current.parentElement;
85
+ if (parentEl) {
86
+ const currentEl = current;
87
+ const siblings = Array.from(parentEl.children);
88
+ const sameTagSiblings = siblings.filter(
89
+ (s) => s.nodeName === currentEl.nodeName
90
+ );
91
+ if (sameTagSiblings.length > 1) {
92
+ const index = siblings.indexOf(currentEl) + 1;
93
+ selector += `:nth-child(${index})`;
94
+ }
95
+ }
96
+ path.unshift(selector);
97
+ current = current.parentElement;
98
+ }
99
+ return path.join(" > ");
100
+ }
101
+ function getBestIdentifier(element) {
102
+ const uiId = element.getAttribute("data-ui-id");
103
+ if (uiId) return uiId;
104
+ const testId = element.getAttribute("data-testid");
105
+ if (testId) return testId;
106
+ const awasId = element.getAttribute("data-awas-element");
107
+ if (awasId) return awasId;
108
+ if (element.id) return element.id;
109
+ return generateCSSSelector(element);
110
+ }
111
+ function createElementIdentifier(element) {
112
+ return {
113
+ uiId: element.getAttribute("data-ui-id") || void 0,
114
+ testId: element.getAttribute("data-testid") || void 0,
115
+ awasId: element.getAttribute("data-awas-element") || void 0,
116
+ htmlId: element.id || void 0,
117
+ xpath: generateXPath(element),
118
+ selector: generateCSSSelector(element)
119
+ };
120
+ }
121
+ function findElementByIdentifier(identifier, root = document) {
122
+ if (typeof identifier === "string") {
123
+ const byUiId = root.querySelector(`[data-ui-id="${identifier}"]`);
124
+ if (byUiId) return byUiId;
125
+ const byTestId = root.querySelector(`[data-testid="${identifier}"]`);
126
+ if (byTestId) return byTestId;
127
+ const byAwasId = root.querySelector(`[data-awas-element="${identifier}"]`);
128
+ if (byAwasId) return byAwasId;
129
+ const byId = root.querySelector(`#${CSS.escape(identifier)}`);
130
+ if (byId) return byId;
131
+ try {
132
+ const bySelector = root.querySelector(identifier);
133
+ if (bySelector) return bySelector;
134
+ } catch {
135
+ }
136
+ try {
137
+ const result = document.evaluate(
138
+ identifier,
139
+ root,
140
+ null,
141
+ XPathResult.FIRST_ORDERED_NODE_TYPE,
142
+ null
143
+ );
144
+ if (result.singleNodeValue instanceof HTMLElement) {
145
+ return result.singleNodeValue;
146
+ }
147
+ } catch {
148
+ }
149
+ return null;
150
+ }
151
+ if (identifier.uiId) {
152
+ const el = root.querySelector(`[data-ui-id="${identifier.uiId}"]`);
153
+ if (el) return el;
154
+ }
155
+ if (identifier.testId) {
156
+ const el = root.querySelector(`[data-testid="${identifier.testId}"]`);
157
+ if (el) return el;
158
+ }
159
+ if (identifier.awasId) {
160
+ const el = root.querySelector(`[data-awas-element="${identifier.awasId}"]`);
161
+ if (el) return el;
162
+ }
163
+ if (identifier.htmlId) {
164
+ const el = root.querySelector(`#${CSS.escape(identifier.htmlId)}`);
165
+ if (el) return el;
166
+ }
167
+ if (identifier.selector) {
168
+ try {
169
+ const el = root.querySelector(identifier.selector);
170
+ if (el) return el;
171
+ } catch {
172
+ }
173
+ }
174
+ if (identifier.xpath) {
175
+ try {
176
+ const result = document.evaluate(
177
+ identifier.xpath,
178
+ root,
179
+ null,
180
+ XPathResult.FIRST_ORDERED_NODE_TYPE,
181
+ null
182
+ );
183
+ if (result.singleNodeValue instanceof HTMLElement) {
184
+ return result.singleNodeValue;
185
+ }
186
+ } catch {
187
+ }
188
+ }
189
+ return null;
190
+ }
191
+ function findAllElementsByIdentifier(pattern, root = document) {
192
+ const results = [];
193
+ try {
194
+ const elements = root.querySelectorAll(pattern);
195
+ results.push(...Array.from(elements));
196
+ if (results.length > 0) return results;
197
+ } catch {
198
+ }
199
+ const partials = [
200
+ `[data-ui-id*="${pattern}"]`,
201
+ `[data-testid*="${pattern}"]`,
202
+ `[data-awas-element*="${pattern}"]`,
203
+ `[id*="${pattern}"]`
204
+ ];
205
+ for (const selector of partials) {
206
+ try {
207
+ const elements = root.querySelectorAll(selector);
208
+ for (const el of elements) {
209
+ if (!results.includes(el)) {
210
+ results.push(el);
211
+ }
212
+ }
213
+ } catch {
214
+ }
215
+ }
216
+ return results;
217
+ }
218
+ function elementMatchesIdentifier(element, identifier) {
219
+ if (typeof identifier === "string") {
220
+ return element.getAttribute("data-ui-id") === identifier || element.getAttribute("data-testid") === identifier || element.getAttribute("data-awas-element") === identifier || element.id === identifier || element.matches(identifier);
221
+ }
222
+ return identifier.uiId && element.getAttribute("data-ui-id") === identifier.uiId || identifier.testId && element.getAttribute("data-testid") === identifier.testId || identifier.awasId && element.getAttribute("data-awas-element") === identifier.awasId || identifier.htmlId && element.id === identifier.htmlId || false;
223
+ }
224
+
225
+ // src/core/registry.ts
226
+ function getElementState(element) {
227
+ const rect = element.getBoundingClientRect();
228
+ const computedStyle = window.getComputedStyle(element);
229
+ const state = {
230
+ visible: isElementVisible(element, rect, computedStyle),
231
+ enabled: !isElementDisabled(element),
232
+ focused: document.activeElement === element,
233
+ rect: {
234
+ x: rect.x,
235
+ y: rect.y,
236
+ width: rect.width,
237
+ height: rect.height,
238
+ top: rect.top,
239
+ right: rect.right,
240
+ bottom: rect.bottom,
241
+ left: rect.left
242
+ },
243
+ textContent: element.textContent?.trim() || void 0,
244
+ computedStyles: {
245
+ display: computedStyle.display,
246
+ visibility: computedStyle.visibility,
247
+ opacity: computedStyle.opacity,
248
+ pointerEvents: computedStyle.pointerEvents
249
+ }
250
+ };
251
+ if (element instanceof HTMLInputElement) {
252
+ state.value = element.value;
253
+ if (element.type === "checkbox" || element.type === "radio") {
254
+ state.checked = element.checked;
255
+ }
256
+ } else if (element instanceof HTMLTextAreaElement) {
257
+ state.value = element.value;
258
+ } else if (element instanceof HTMLSelectElement) {
259
+ state.value = element.value;
260
+ state.selectedOptions = Array.from(element.selectedOptions).map((opt) => opt.value);
261
+ }
262
+ return state;
263
+ }
264
+ function isElementVisible(element, rect, style) {
265
+ if (rect.width === 0 || rect.height === 0) return false;
266
+ if (style.display === "none") return false;
267
+ if (style.visibility === "hidden") return false;
268
+ if (parseFloat(style.opacity) === 0) return false;
269
+ const inViewport = rect.top < window.innerHeight && rect.bottom > 0 && rect.left < window.innerWidth && rect.right > 0;
270
+ return inViewport;
271
+ }
272
+ function isElementDisabled(element) {
273
+ if ("disabled" in element && element.disabled) {
274
+ return true;
275
+ }
276
+ if (element.getAttribute("aria-disabled") === "true") {
277
+ return true;
278
+ }
279
+ return false;
280
+ }
281
+ function inferActions(type) {
282
+ const baseActions = ["focus", "blur", "hover"];
283
+ switch (type) {
284
+ case "button":
285
+ return [...baseActions, "click", "doubleClick", "rightClick"];
286
+ case "input":
287
+ return [...baseActions, "click", "type", "clear"];
288
+ case "textarea":
289
+ return [...baseActions, "click", "type", "clear"];
290
+ case "select":
291
+ return [...baseActions, "click", "select"];
292
+ case "checkbox":
293
+ return [...baseActions, "click", "check", "uncheck", "toggle"];
294
+ case "radio":
295
+ return [...baseActions, "click", "check"];
296
+ case "link":
297
+ return [...baseActions, "click"];
298
+ case "form":
299
+ return ["focus", "blur"];
300
+ case "menu":
301
+ case "menuitem":
302
+ return [...baseActions, "click"];
303
+ case "tab":
304
+ return [...baseActions, "click"];
305
+ case "dialog":
306
+ return ["focus", "blur"];
307
+ case "custom":
308
+ default:
309
+ return [...baseActions, "click"];
310
+ }
311
+ }
312
+ function inferElementType(element) {
313
+ const tagName = element.tagName.toLowerCase();
314
+ const role = element.getAttribute("role");
315
+ if (role) {
316
+ switch (role) {
317
+ case "button":
318
+ return "button";
319
+ case "textbox":
320
+ return "input";
321
+ case "checkbox":
322
+ return "checkbox";
323
+ case "radio":
324
+ return "radio";
325
+ case "link":
326
+ return "link";
327
+ case "listbox":
328
+ case "combobox":
329
+ return "select";
330
+ case "menu":
331
+ return "menu";
332
+ case "menuitem":
333
+ return "menuitem";
334
+ case "tab":
335
+ return "tab";
336
+ case "dialog":
337
+ return "dialog";
338
+ }
339
+ }
340
+ switch (tagName) {
341
+ case "button":
342
+ return "button";
343
+ case "input": {
344
+ const inputType = element.type;
345
+ if (inputType === "checkbox") return "checkbox";
346
+ if (inputType === "radio") return "radio";
347
+ if (inputType === "submit" || inputType === "button") return "button";
348
+ return "input";
349
+ }
350
+ case "textarea":
351
+ return "textarea";
352
+ case "select":
353
+ return "select";
354
+ case "a":
355
+ return "link";
356
+ case "form":
357
+ return "form";
358
+ default:
359
+ return "custom";
360
+ }
361
+ }
362
+ var UIBridgeRegistry = class {
363
+ constructor(options = {}) {
364
+ this.elements = /* @__PURE__ */ new Map();
365
+ this.components = /* @__PURE__ */ new Map();
366
+ this.workflows = /* @__PURE__ */ new Map();
367
+ this.eventListeners = /* @__PURE__ */ new Map();
368
+ // State management
369
+ this.states = /* @__PURE__ */ new Map();
370
+ this.stateGroups = /* @__PURE__ */ new Map();
371
+ this.transitions = /* @__PURE__ */ new Map();
372
+ this.activeStates = /* @__PURE__ */ new Set();
373
+ this.options = options;
374
+ }
375
+ /**
376
+ * Emit an event
377
+ */
378
+ emit(type, data) {
379
+ const event = {
380
+ type,
381
+ timestamp: Date.now(),
382
+ data
383
+ };
384
+ this.options.onEvent?.(event);
385
+ const listeners = this.eventListeners.get(type);
386
+ if (listeners) {
387
+ for (const listener of listeners) {
388
+ try {
389
+ listener(event);
390
+ } catch (error) {
391
+ console.error(`Error in event listener for ${type}:`, error);
392
+ }
393
+ }
394
+ }
395
+ if (this.options.verbose) {
396
+ console.log("[UIBridge]", type, data);
397
+ }
398
+ }
399
+ /**
400
+ * Register an event listener
401
+ */
402
+ on(type, listener) {
403
+ if (!this.eventListeners.has(type)) {
404
+ this.eventListeners.set(type, /* @__PURE__ */ new Set());
405
+ }
406
+ this.eventListeners.get(type).add(listener);
407
+ return () => {
408
+ this.eventListeners.get(type)?.delete(listener);
409
+ };
410
+ }
411
+ /**
412
+ * Remove an event listener
413
+ */
414
+ off(type, listener) {
415
+ this.eventListeners.get(type)?.delete(listener);
416
+ }
417
+ /**
418
+ * Register an element
419
+ */
420
+ registerElement(id, element, options = {}) {
421
+ const type = options.type ?? inferElementType(element);
422
+ const actions = options.actions ?? inferActions(type);
423
+ element.setAttribute("data-ui-id", id);
424
+ const registered = {
425
+ id,
426
+ element,
427
+ type,
428
+ label: options.label,
429
+ actions,
430
+ customActions: options.customActions,
431
+ getState: () => getElementState(element),
432
+ getIdentifier: () => createElementIdentifier(element),
433
+ registeredAt: Date.now(),
434
+ mounted: true
435
+ };
436
+ this.elements.set(id, registered);
437
+ this.emit("element:registered", { id, type, label: options.label });
438
+ return registered;
439
+ }
440
+ /**
441
+ * Unregister an element
442
+ */
443
+ unregisterElement(id) {
444
+ const registered = this.elements.get(id);
445
+ if (registered) {
446
+ registered.mounted = false;
447
+ registered.element.removeAttribute("data-ui-id");
448
+ this.elements.delete(id);
449
+ this.emit("element:unregistered", { id });
450
+ return true;
451
+ }
452
+ return false;
453
+ }
454
+ /**
455
+ * Get a registered element
456
+ */
457
+ getElement(id) {
458
+ return this.elements.get(id);
459
+ }
460
+ /**
461
+ * Get all registered elements
462
+ */
463
+ getAllElements() {
464
+ return Array.from(this.elements.values());
465
+ }
466
+ /**
467
+ * Find element by DOM element reference
468
+ */
469
+ findByDOMElement(element) {
470
+ for (const registered of this.elements.values()) {
471
+ if (registered.element === element) {
472
+ return registered;
473
+ }
474
+ }
475
+ return void 0;
476
+ }
477
+ /**
478
+ * Register a component
479
+ */
480
+ registerComponent(id, options) {
481
+ const registered = {
482
+ id,
483
+ name: options.name,
484
+ description: options.description,
485
+ actions: options.actions?.map((a) => ({
486
+ id: a.id,
487
+ label: a.label,
488
+ description: a.description,
489
+ handler: a.handler
490
+ })) ?? [],
491
+ elementIds: options.elementIds,
492
+ registeredAt: Date.now(),
493
+ mounted: true
494
+ };
495
+ this.components.set(id, registered);
496
+ this.emit("component:registered", { id, name: options.name });
497
+ return registered;
498
+ }
499
+ /**
500
+ * Unregister a component
501
+ */
502
+ unregisterComponent(id) {
503
+ const component = this.components.get(id);
504
+ if (component) {
505
+ component.mounted = false;
506
+ this.components.delete(id);
507
+ this.emit("component:unregistered", { id });
508
+ return true;
509
+ }
510
+ return false;
511
+ }
512
+ /**
513
+ * Get a registered component
514
+ */
515
+ getComponent(id) {
516
+ return this.components.get(id);
517
+ }
518
+ /**
519
+ * Get all registered components
520
+ */
521
+ getAllComponents() {
522
+ return Array.from(this.components.values());
523
+ }
524
+ /**
525
+ * Register a workflow
526
+ */
527
+ registerWorkflow(workflow) {
528
+ this.workflows.set(workflow.id, workflow);
529
+ return workflow;
530
+ }
531
+ /**
532
+ * Unregister a workflow
533
+ */
534
+ unregisterWorkflow(id) {
535
+ return this.workflows.delete(id);
536
+ }
537
+ /**
538
+ * Get a workflow
539
+ */
540
+ getWorkflow(id) {
541
+ return this.workflows.get(id);
542
+ }
543
+ /**
544
+ * Get all workflows
545
+ */
546
+ getAllWorkflows() {
547
+ return Array.from(this.workflows.values());
548
+ }
549
+ // ==========================================================================
550
+ // State Management
551
+ // ==========================================================================
552
+ /**
553
+ * Register a state
554
+ */
555
+ registerState(state) {
556
+ this.states.set(state.id, state);
557
+ this.emit("element:registered", { id: state.id, type: "state", name: state.name });
558
+ return state;
559
+ }
560
+ /**
561
+ * Unregister a state
562
+ */
563
+ unregisterState(id) {
564
+ const state = this.states.get(id);
565
+ if (state) {
566
+ this.activeStates.delete(id);
567
+ this.states.delete(id);
568
+ this.emit("element:unregistered", { id, type: "state" });
569
+ return true;
570
+ }
571
+ return false;
572
+ }
573
+ /**
574
+ * Get a registered state
575
+ */
576
+ getState(id) {
577
+ return this.states.get(id);
578
+ }
579
+ /**
580
+ * Get all registered states
581
+ */
582
+ getAllStates() {
583
+ return Array.from(this.states.values());
584
+ }
585
+ /**
586
+ * Register a state group
587
+ */
588
+ registerStateGroup(group) {
589
+ this.stateGroups.set(group.id, group);
590
+ return group;
591
+ }
592
+ /**
593
+ * Unregister a state group
594
+ */
595
+ unregisterStateGroup(id) {
596
+ return this.stateGroups.delete(id);
597
+ }
598
+ /**
599
+ * Get a state group
600
+ */
601
+ getStateGroup(id) {
602
+ return this.stateGroups.get(id);
603
+ }
604
+ /**
605
+ * Get all state groups
606
+ */
607
+ getAllStateGroups() {
608
+ return Array.from(this.stateGroups.values());
609
+ }
610
+ /**
611
+ * Register a transition
612
+ */
613
+ registerTransition(transition) {
614
+ this.transitions.set(transition.id, transition);
615
+ return transition;
616
+ }
617
+ /**
618
+ * Unregister a transition
619
+ */
620
+ unregisterTransition(id) {
621
+ return this.transitions.delete(id);
622
+ }
623
+ /**
624
+ * Get a transition
625
+ */
626
+ getTransition(id) {
627
+ return this.transitions.get(id);
628
+ }
629
+ /**
630
+ * Get all transitions
631
+ */
632
+ getAllTransitions() {
633
+ return Array.from(this.transitions.values());
634
+ }
635
+ /**
636
+ * Get currently active states
637
+ */
638
+ getActiveStates() {
639
+ return Array.from(this.activeStates);
640
+ }
641
+ /**
642
+ * Check if a state is active
643
+ */
644
+ isStateActive(id) {
645
+ return this.activeStates.has(id);
646
+ }
647
+ /**
648
+ * Activate a state
649
+ */
650
+ activateState(id) {
651
+ const state = this.states.get(id);
652
+ if (!state) {
653
+ return false;
654
+ }
655
+ for (const activeId of this.activeStates) {
656
+ const activeState = this.states.get(activeId);
657
+ if (activeState?.blocking && activeState.id !== id) {
658
+ return false;
659
+ }
660
+ if (activeState?.blocks?.includes(id)) {
661
+ return false;
662
+ }
663
+ }
664
+ const wasActive = this.activeStates.has(id);
665
+ this.activeStates.add(id);
666
+ if (!wasActive) {
667
+ this.emit("element:stateChanged", {
668
+ stateId: id,
669
+ active: true,
670
+ activeStates: this.getActiveStates()
671
+ });
672
+ }
673
+ return true;
674
+ }
675
+ /**
676
+ * Deactivate a state
677
+ */
678
+ deactivateState(id) {
679
+ const wasActive = this.activeStates.has(id);
680
+ this.activeStates.delete(id);
681
+ if (wasActive) {
682
+ this.emit("element:stateChanged", {
683
+ stateId: id,
684
+ active: false,
685
+ activeStates: this.getActiveStates()
686
+ });
687
+ }
688
+ return wasActive;
689
+ }
690
+ /**
691
+ * Activate multiple states
692
+ */
693
+ activateStates(ids) {
694
+ const activated = [];
695
+ for (const id of ids) {
696
+ if (this.activateState(id)) {
697
+ activated.push(id);
698
+ }
699
+ }
700
+ return activated;
701
+ }
702
+ /**
703
+ * Deactivate multiple states
704
+ */
705
+ deactivateStates(ids) {
706
+ const deactivated = [];
707
+ for (const id of ids) {
708
+ if (this.deactivateState(id)) {
709
+ deactivated.push(id);
710
+ }
711
+ }
712
+ return deactivated;
713
+ }
714
+ /**
715
+ * Activate a state group (all states in the group)
716
+ */
717
+ activateStateGroup(groupId) {
718
+ const group = this.stateGroups.get(groupId);
719
+ if (!group) return [];
720
+ return this.activateStates(group.states);
721
+ }
722
+ /**
723
+ * Deactivate a state group (all states in the group)
724
+ */
725
+ deactivateStateGroup(groupId) {
726
+ const group = this.stateGroups.get(groupId);
727
+ if (!group) return [];
728
+ return this.deactivateStates(group.states);
729
+ }
730
+ /**
731
+ * Check if a transition can be executed from current state
732
+ */
733
+ canExecuteTransition(transitionId) {
734
+ const transition = this.transitions.get(transitionId);
735
+ if (!transition) return false;
736
+ return transition.fromStates.some((stateId) => this.activeStates.has(stateId));
737
+ }
738
+ /**
739
+ * Execute a transition
740
+ */
741
+ async executeTransition(transitionId) {
742
+ const startTime = performance.now();
743
+ const transition = this.transitions.get(transitionId);
744
+ if (!transition) {
745
+ return {
746
+ success: false,
747
+ activatedStates: [],
748
+ deactivatedStates: [],
749
+ error: `Transition not found: ${transitionId}`,
750
+ durationMs: performance.now() - startTime
751
+ };
752
+ }
753
+ if (!this.canExecuteTransition(transitionId)) {
754
+ return {
755
+ success: false,
756
+ activatedStates: [],
757
+ deactivatedStates: [],
758
+ error: "Precondition not met: none of the fromStates are active",
759
+ failedPhase: "precondition",
760
+ durationMs: performance.now() - startTime
761
+ };
762
+ }
763
+ try {
764
+ const deactivated = this.deactivateStates(transition.exitStates);
765
+ if (transition.exitGroups) {
766
+ for (const groupId of transition.exitGroups) {
767
+ deactivated.push(...this.deactivateStateGroup(groupId));
768
+ }
769
+ }
770
+ const activated = this.activateStates(transition.activateStates);
771
+ if (transition.activateGroups) {
772
+ for (const groupId of transition.activateGroups) {
773
+ activated.push(...this.activateStateGroup(groupId));
774
+ }
775
+ }
776
+ return {
777
+ success: true,
778
+ activatedStates: activated,
779
+ deactivatedStates: deactivated,
780
+ durationMs: performance.now() - startTime
781
+ };
782
+ } catch (error) {
783
+ return {
784
+ success: false,
785
+ activatedStates: [],
786
+ deactivatedStates: [],
787
+ error: error instanceof Error ? error.message : String(error),
788
+ failedPhase: "execution",
789
+ durationMs: performance.now() - startTime
790
+ };
791
+ }
792
+ }
793
+ /**
794
+ * Find a path from current state to target states
795
+ *
796
+ * Uses a simple BFS algorithm for pathfinding.
797
+ * For more advanced pathfinding (Dijkstra, A*), use the Python state manager service.
798
+ */
799
+ findPath(targetStates) {
800
+ if (targetStates.every((t) => this.activeStates.has(t))) {
801
+ return {
802
+ found: true,
803
+ transitions: [],
804
+ totalCost: 0,
805
+ targetStates,
806
+ estimatedSteps: 0
807
+ };
808
+ }
809
+ const queue = [
810
+ { activeStates: new Set(this.activeStates), path: [], cost: 0 }
811
+ ];
812
+ const visited = /* @__PURE__ */ new Set();
813
+ while (queue.length > 0) {
814
+ const current = queue.shift();
815
+ const stateKey = Array.from(current.activeStates).sort().join(",");
816
+ if (visited.has(stateKey)) continue;
817
+ visited.add(stateKey);
818
+ if (targetStates.every((t) => current.activeStates.has(t))) {
819
+ return {
820
+ found: true,
821
+ transitions: current.path,
822
+ totalCost: current.cost,
823
+ targetStates,
824
+ estimatedSteps: current.path.length
825
+ };
826
+ }
827
+ for (const transition of this.transitions.values()) {
828
+ const canExecute = transition.fromStates.some((s) => current.activeStates.has(s));
829
+ if (!canExecute) continue;
830
+ const newActive = new Set(current.activeStates);
831
+ for (const s of transition.exitStates) newActive.delete(s);
832
+ for (const s of transition.activateStates) newActive.add(s);
833
+ const newCost = current.cost + (transition.pathCost ?? 1);
834
+ queue.push({
835
+ activeStates: newActive,
836
+ path: [...current.path, transition.id],
837
+ cost: newCost
838
+ });
839
+ }
840
+ }
841
+ return {
842
+ found: false,
843
+ transitions: [],
844
+ totalCost: 0,
845
+ targetStates,
846
+ estimatedSteps: 0
847
+ };
848
+ }
849
+ /**
850
+ * Navigate to target states using pathfinding
851
+ */
852
+ async navigateTo(targetStates) {
853
+ const startTime = performance.now();
854
+ const path = this.findPath(targetStates);
855
+ if (!path.found) {
856
+ return {
857
+ success: false,
858
+ path,
859
+ executedTransitions: [],
860
+ finalActiveStates: this.getActiveStates(),
861
+ error: `No path found to target states: ${targetStates.join(", ")}`,
862
+ durationMs: performance.now() - startTime
863
+ };
864
+ }
865
+ const executedTransitions = [];
866
+ for (const transitionId of path.transitions) {
867
+ const result = await this.executeTransition(transitionId);
868
+ if (!result.success) {
869
+ return {
870
+ success: false,
871
+ path,
872
+ executedTransitions,
873
+ finalActiveStates: this.getActiveStates(),
874
+ error: result.error,
875
+ durationMs: performance.now() - startTime
876
+ };
877
+ }
878
+ executedTransitions.push(transitionId);
879
+ }
880
+ return {
881
+ success: true,
882
+ path,
883
+ executedTransitions,
884
+ finalActiveStates: this.getActiveStates(),
885
+ durationMs: performance.now() - startTime
886
+ };
887
+ }
888
+ /**
889
+ * Create a state snapshot
890
+ */
891
+ createStateSnapshot() {
892
+ return {
893
+ timestamp: Date.now(),
894
+ activeStates: this.getActiveStates(),
895
+ states: this.getAllStates(),
896
+ groups: this.getAllStateGroups(),
897
+ transitions: this.getAllTransitions()
898
+ };
899
+ }
900
+ /**
901
+ * Create a snapshot of the current state
902
+ */
903
+ createSnapshot() {
904
+ return {
905
+ timestamp: Date.now(),
906
+ elements: this.getAllElements().map((el) => ({
907
+ id: el.id,
908
+ type: el.type,
909
+ label: el.label,
910
+ identifier: el.getIdentifier(),
911
+ state: el.getState(),
912
+ actions: el.actions,
913
+ customActions: el.customActions ? Object.keys(el.customActions) : void 0
914
+ })),
915
+ components: this.getAllComponents().map((comp) => ({
916
+ id: comp.id,
917
+ name: comp.name,
918
+ description: comp.description,
919
+ actions: comp.actions.map((a) => a.id),
920
+ elementIds: comp.elementIds
921
+ })),
922
+ workflows: this.getAllWorkflows().map((wf) => ({
923
+ id: wf.id,
924
+ name: wf.name,
925
+ description: wf.description,
926
+ stepCount: wf.steps.length
927
+ }))
928
+ };
929
+ }
930
+ /**
931
+ * Clear all registrations
932
+ */
933
+ clear() {
934
+ this.elements.clear();
935
+ this.components.clear();
936
+ this.workflows.clear();
937
+ this.eventListeners.clear();
938
+ this.states.clear();
939
+ this.stateGroups.clear();
940
+ this.transitions.clear();
941
+ this.activeStates.clear();
942
+ }
943
+ /**
944
+ * Get registry statistics
945
+ */
946
+ getStats() {
947
+ const elements = this.getAllElements();
948
+ const components = this.getAllComponents();
949
+ return {
950
+ elementCount: elements.length,
951
+ componentCount: components.length,
952
+ workflowCount: this.workflows.size,
953
+ mountedElementCount: elements.filter((e) => e.mounted).length,
954
+ mountedComponentCount: components.filter((c) => c.mounted).length,
955
+ stateCount: this.states.size,
956
+ stateGroupCount: this.stateGroups.size,
957
+ transitionCount: this.transitions.size,
958
+ activeStateCount: this.activeStates.size
959
+ };
960
+ }
961
+ };
962
+ var globalRegistry = null;
963
+ function getGlobalRegistry() {
964
+ if (!globalRegistry) {
965
+ globalRegistry = new UIBridgeRegistry();
966
+ }
967
+ return globalRegistry;
968
+ }
969
+ function setGlobalRegistry(registry) {
970
+ globalRegistry = registry;
971
+ }
972
+ function resetGlobalRegistry() {
973
+ globalRegistry?.clear();
974
+ globalRegistry = null;
975
+ }
976
+
977
+ // src/core/websocket-client.ts
978
+ function generateId() {
979
+ return `${Date.now()}-${Math.random().toString(36).slice(2, 11)}`;
980
+ }
981
+ var UIBridgeWSClient = class {
982
+ constructor(config) {
983
+ this.ws = null;
984
+ this.state = "disconnected";
985
+ this.clientId = null;
986
+ this.reconnectAttempts = 0;
987
+ this.reconnectTimer = null;
988
+ this.pingTimer = null;
989
+ this.pendingRequests = /* @__PURE__ */ new Map();
990
+ // Event listeners
991
+ this.connectionListeners = /* @__PURE__ */ new Set();
992
+ this.eventListeners = /* @__PURE__ */ new Map();
993
+ this.errorListeners = /* @__PURE__ */ new Set();
994
+ // Current subscriptions
995
+ this.subscriptions = {};
996
+ this.config = {
997
+ url: config.url,
998
+ autoReconnect: config.autoReconnect ?? true,
999
+ reconnectDelay: config.reconnectDelay ?? 1e3,
1000
+ maxReconnectAttempts: config.maxReconnectAttempts ?? 10,
1001
+ pingInterval: config.pingInterval ?? 3e4,
1002
+ connectionTimeout: config.connectionTimeout ?? 1e4
1003
+ };
1004
+ }
1005
+ /**
1006
+ * Get current connection state
1007
+ */
1008
+ get connectionState() {
1009
+ return this.state;
1010
+ }
1011
+ /**
1012
+ * Get assigned client ID
1013
+ */
1014
+ get id() {
1015
+ return this.clientId;
1016
+ }
1017
+ /**
1018
+ * Connect to the WebSocket server
1019
+ */
1020
+ connect() {
1021
+ return new Promise((resolve, reject) => {
1022
+ if (this.ws && this.state === "connected") {
1023
+ resolve();
1024
+ return;
1025
+ }
1026
+ this.setState("connecting");
1027
+ try {
1028
+ this.ws = new WebSocket(this.config.url);
1029
+ } catch (error) {
1030
+ this.setState("disconnected");
1031
+ reject(error);
1032
+ return;
1033
+ }
1034
+ const connectionTimeout = setTimeout(() => {
1035
+ if (this.state === "connecting") {
1036
+ this.ws?.close();
1037
+ this.setState("disconnected");
1038
+ reject(new Error("Connection timeout"));
1039
+ }
1040
+ }, this.config.connectionTimeout);
1041
+ this.ws.onopen = () => {
1042
+ clearTimeout(connectionTimeout);
1043
+ };
1044
+ this.ws.onmessage = (event) => {
1045
+ try {
1046
+ const message = JSON.parse(event.data);
1047
+ this.handleMessage(message);
1048
+ if (message.type === "welcome") {
1049
+ clearTimeout(connectionTimeout);
1050
+ this.reconnectAttempts = 0;
1051
+ this.setState("connected");
1052
+ this.startPingInterval();
1053
+ if (this.subscriptions.events?.length || this.subscriptions.elementIds?.length || this.subscriptions.componentIds?.length) {
1054
+ this.subscribe(this.subscriptions);
1055
+ }
1056
+ resolve();
1057
+ }
1058
+ } catch (error) {
1059
+ console.error("Failed to parse WebSocket message:", error);
1060
+ }
1061
+ };
1062
+ this.ws.onerror = (_event) => {
1063
+ clearTimeout(connectionTimeout);
1064
+ const error = new Error("WebSocket error");
1065
+ this.notifyError(error);
1066
+ if (this.state === "connecting") {
1067
+ reject(error);
1068
+ }
1069
+ };
1070
+ this.ws.onclose = () => {
1071
+ clearTimeout(connectionTimeout);
1072
+ this.stopPingInterval();
1073
+ this.clientId = null;
1074
+ const wasConnected = this.state === "connected";
1075
+ this.setState("disconnected");
1076
+ for (const [_id, pending] of this.pendingRequests) {
1077
+ clearTimeout(pending.timeout);
1078
+ pending.reject(new Error("Connection closed"));
1079
+ }
1080
+ this.pendingRequests.clear();
1081
+ if (wasConnected && this.config.autoReconnect && (this.config.maxReconnectAttempts === 0 || this.reconnectAttempts < this.config.maxReconnectAttempts)) {
1082
+ this.scheduleReconnect();
1083
+ }
1084
+ };
1085
+ });
1086
+ }
1087
+ /**
1088
+ * Disconnect from the server
1089
+ */
1090
+ disconnect() {
1091
+ if (this.reconnectTimer) {
1092
+ clearTimeout(this.reconnectTimer);
1093
+ this.reconnectTimer = null;
1094
+ }
1095
+ this.stopPingInterval();
1096
+ if (this.ws) {
1097
+ this.ws.close();
1098
+ this.ws = null;
1099
+ }
1100
+ this.setState("disconnected");
1101
+ }
1102
+ /**
1103
+ * Subscribe to events
1104
+ */
1105
+ async subscribe(options) {
1106
+ this.subscriptions = { ...this.subscriptions, ...options };
1107
+ const response = await this.sendRequest({
1108
+ id: generateId(),
1109
+ type: "subscribe",
1110
+ timestamp: Date.now(),
1111
+ payload: options
1112
+ });
1113
+ return response.events;
1114
+ }
1115
+ /**
1116
+ * Unsubscribe from events
1117
+ */
1118
+ async unsubscribe(events) {
1119
+ if (events) {
1120
+ this.subscriptions.events = this.subscriptions.events?.filter((e) => !events.includes(e));
1121
+ } else {
1122
+ this.subscriptions = {};
1123
+ }
1124
+ const response = await this.sendRequest({
1125
+ id: generateId(),
1126
+ type: "unsubscribe",
1127
+ timestamp: Date.now(),
1128
+ payload: { events }
1129
+ });
1130
+ return response.events;
1131
+ }
1132
+ /**
1133
+ * Find elements
1134
+ */
1135
+ async find(options) {
1136
+ const response = await this.sendRequest({
1137
+ id: generateId(),
1138
+ type: "find",
1139
+ timestamp: Date.now(),
1140
+ payload: options
1141
+ });
1142
+ return response.elements;
1143
+ }
1144
+ /**
1145
+ * Discover elements
1146
+ * @deprecated Use find() instead
1147
+ */
1148
+ async discover(options) {
1149
+ return this.find(options);
1150
+ }
1151
+ /**
1152
+ * Get element details
1153
+ */
1154
+ async getElement(elementId, includeState = true) {
1155
+ const response = await this.sendRequest({
1156
+ id: generateId(),
1157
+ type: "getElement",
1158
+ timestamp: Date.now(),
1159
+ payload: { elementId, includeState }
1160
+ });
1161
+ return response.element;
1162
+ }
1163
+ /**
1164
+ * Get full snapshot
1165
+ */
1166
+ async getSnapshot() {
1167
+ const response = await this.sendRequest({
1168
+ id: generateId(),
1169
+ type: "getSnapshot",
1170
+ timestamp: Date.now()
1171
+ });
1172
+ return response;
1173
+ }
1174
+ /**
1175
+ * Execute action on an element
1176
+ */
1177
+ async executeAction(elementId, action) {
1178
+ const response = await this.sendRequest({
1179
+ id: generateId(),
1180
+ type: "executeAction",
1181
+ timestamp: Date.now(),
1182
+ payload: { elementId, action }
1183
+ });
1184
+ return response;
1185
+ }
1186
+ /**
1187
+ * Execute component action
1188
+ */
1189
+ async executeComponentAction(componentId, action, params) {
1190
+ const response = await this.sendRequest({
1191
+ id: generateId(),
1192
+ type: "executeComponentAction",
1193
+ timestamp: Date.now(),
1194
+ payload: { componentId, action, params }
1195
+ });
1196
+ return response;
1197
+ }
1198
+ /**
1199
+ * Execute workflow with optional progress streaming
1200
+ */
1201
+ async executeWorkflow(workflowId, params, onProgress) {
1202
+ const id = generateId();
1203
+ const progressHandler = onProgress ? (message) => {
1204
+ if (message.type === "workflowProgress" && message.requestId === id) {
1205
+ onProgress({
1206
+ currentStep: message.payload.currentStep,
1207
+ totalSteps: message.payload.totalSteps,
1208
+ step: {
1209
+ id: message.payload.step.id,
1210
+ status: message.payload.step.status
1211
+ }
1212
+ });
1213
+ }
1214
+ } : void 0;
1215
+ const response = await this.sendRequest(
1216
+ {
1217
+ id,
1218
+ type: "executeWorkflow",
1219
+ timestamp: Date.now(),
1220
+ payload: { workflowId, params, streamProgress: !!onProgress }
1221
+ },
1222
+ progressHandler
1223
+ );
1224
+ return response;
1225
+ }
1226
+ /**
1227
+ * Add connection state listener
1228
+ */
1229
+ onConnectionChange(listener) {
1230
+ this.connectionListeners.add(listener);
1231
+ return () => this.connectionListeners.delete(listener);
1232
+ }
1233
+ /**
1234
+ * Add event listener
1235
+ */
1236
+ onEvent(eventType, listener) {
1237
+ if (!this.eventListeners.has(eventType)) {
1238
+ this.eventListeners.set(eventType, /* @__PURE__ */ new Set());
1239
+ }
1240
+ this.eventListeners.get(eventType).add(listener);
1241
+ return () => this.eventListeners.get(eventType)?.delete(listener);
1242
+ }
1243
+ /**
1244
+ * Add error listener
1245
+ */
1246
+ onError(listener) {
1247
+ this.errorListeners.add(listener);
1248
+ return () => this.errorListeners.delete(listener);
1249
+ }
1250
+ // Private methods
1251
+ setState(state) {
1252
+ this.state = state;
1253
+ for (const listener of this.connectionListeners) {
1254
+ try {
1255
+ listener(state);
1256
+ } catch (error) {
1257
+ console.error("Connection listener error:", error);
1258
+ }
1259
+ }
1260
+ }
1261
+ handleMessage(message) {
1262
+ switch (message.type) {
1263
+ case "welcome":
1264
+ this.clientId = message.payload.clientId;
1265
+ break;
1266
+ case "pong":
1267
+ break;
1268
+ case "subscribed":
1269
+ case "unsubscribed":
1270
+ break;
1271
+ case "event":
1272
+ this.notifyEvent(message.payload);
1273
+ break;
1274
+ case "response":
1275
+ this.handleResponse(message);
1276
+ break;
1277
+ case "error":
1278
+ if (message.requestId) {
1279
+ this.handleResponse({
1280
+ ...message,
1281
+ type: "response",
1282
+ requestId: message.requestId,
1283
+ payload: {
1284
+ success: false,
1285
+ error: message.payload.message
1286
+ }
1287
+ });
1288
+ } else {
1289
+ this.notifyError(new Error(message.payload.message));
1290
+ }
1291
+ break;
1292
+ }
1293
+ }
1294
+ handleResponse(message) {
1295
+ const pending = this.pendingRequests.get(message.requestId);
1296
+ if (!pending) return;
1297
+ clearTimeout(pending.timeout);
1298
+ this.pendingRequests.delete(message.requestId);
1299
+ if (message.type === "response") {
1300
+ if (message.payload.success) {
1301
+ pending.resolve(message.payload.data);
1302
+ } else {
1303
+ pending.reject(new Error(message.payload.error || "Request failed"));
1304
+ }
1305
+ }
1306
+ }
1307
+ notifyEvent(event) {
1308
+ const typeListeners = this.eventListeners.get(event.type);
1309
+ if (typeListeners) {
1310
+ for (const listener of typeListeners) {
1311
+ try {
1312
+ listener(event);
1313
+ } catch (error) {
1314
+ console.error("Event listener error:", error);
1315
+ }
1316
+ }
1317
+ }
1318
+ const wildcardListeners = this.eventListeners.get("*");
1319
+ if (wildcardListeners) {
1320
+ for (const listener of wildcardListeners) {
1321
+ try {
1322
+ listener(event);
1323
+ } catch (error) {
1324
+ console.error("Event listener error:", error);
1325
+ }
1326
+ }
1327
+ }
1328
+ }
1329
+ notifyError(error) {
1330
+ for (const listener of this.errorListeners) {
1331
+ try {
1332
+ listener(error);
1333
+ } catch (e) {
1334
+ console.error("Error listener error:", e);
1335
+ }
1336
+ }
1337
+ }
1338
+ sendRequest(message, progressHandler) {
1339
+ return new Promise((resolve, reject) => {
1340
+ if (!this.ws || this.state !== "connected") {
1341
+ reject(new Error("Not connected"));
1342
+ return;
1343
+ }
1344
+ const timeout = setTimeout(() => {
1345
+ this.pendingRequests.delete(message.id);
1346
+ reject(new Error("Request timeout"));
1347
+ }, 3e4);
1348
+ this.pendingRequests.set(message.id, {
1349
+ resolve,
1350
+ reject,
1351
+ timeout
1352
+ });
1353
+ if (progressHandler && this.ws) {
1354
+ const originalHandler = this.ws.onmessage;
1355
+ const wsRef = this.ws;
1356
+ const wrappedHandler = (event) => {
1357
+ try {
1358
+ const msg = JSON.parse(event.data);
1359
+ if (msg.type === "workflowProgress") {
1360
+ progressHandler(msg);
1361
+ }
1362
+ } catch {
1363
+ }
1364
+ if (originalHandler) {
1365
+ originalHandler.call(wsRef, event);
1366
+ }
1367
+ };
1368
+ this.ws.onmessage = wrappedHandler;
1369
+ }
1370
+ this.ws.send(JSON.stringify(message));
1371
+ });
1372
+ }
1373
+ scheduleReconnect() {
1374
+ if (this.reconnectTimer) return;
1375
+ this.setState("reconnecting");
1376
+ this.reconnectAttempts++;
1377
+ const delay = Math.min(
1378
+ this.config.reconnectDelay * Math.pow(2, this.reconnectAttempts - 1),
1379
+ 3e4
1380
+ );
1381
+ this.reconnectTimer = setTimeout(() => {
1382
+ this.reconnectTimer = null;
1383
+ this.connect().catch(() => {
1384
+ });
1385
+ }, delay);
1386
+ }
1387
+ startPingInterval() {
1388
+ if (this.config.pingInterval <= 0) return;
1389
+ this.pingTimer = setInterval(() => {
1390
+ if (this.ws && this.state === "connected") {
1391
+ this.ws.send(
1392
+ JSON.stringify({
1393
+ id: generateId(),
1394
+ type: "ping",
1395
+ timestamp: Date.now()
1396
+ })
1397
+ );
1398
+ }
1399
+ }, this.config.pingInterval);
1400
+ }
1401
+ stopPingInterval() {
1402
+ if (this.pingTimer) {
1403
+ clearInterval(this.pingTimer);
1404
+ this.pingTimer = null;
1405
+ }
1406
+ }
1407
+ };
1408
+ function createWSClient(config) {
1409
+ return new UIBridgeWSClient(config);
1410
+ }
1411
+
1412
+ // src/control/action-executor.ts
1413
+ var DEFAULT_WAIT_OPTIONS = {
1414
+ visible: true,
1415
+ enabled: true,
1416
+ focused: false,
1417
+ state: {},
1418
+ timeout: 1e4,
1419
+ interval: 100
1420
+ };
1421
+ function getElementState2(element) {
1422
+ const rect = element.getBoundingClientRect();
1423
+ const style = window.getComputedStyle(element);
1424
+ const state = {
1425
+ visible: isVisible(element, rect, style),
1426
+ enabled: !isDisabled(element),
1427
+ focused: document.activeElement === element,
1428
+ rect: {
1429
+ x: rect.x,
1430
+ y: rect.y,
1431
+ width: rect.width,
1432
+ height: rect.height,
1433
+ top: rect.top,
1434
+ right: rect.right,
1435
+ bottom: rect.bottom,
1436
+ left: rect.left
1437
+ },
1438
+ computedStyles: {
1439
+ display: style.display,
1440
+ visibility: style.visibility,
1441
+ opacity: style.opacity,
1442
+ pointerEvents: style.pointerEvents
1443
+ }
1444
+ };
1445
+ if (element instanceof HTMLInputElement) {
1446
+ state.value = element.value;
1447
+ if (element.type === "checkbox" || element.type === "radio") {
1448
+ state.checked = element.checked;
1449
+ }
1450
+ } else if (element instanceof HTMLTextAreaElement) {
1451
+ state.value = element.value;
1452
+ } else if (element instanceof HTMLSelectElement) {
1453
+ state.value = element.value;
1454
+ state.selectedOptions = Array.from(element.selectedOptions).map((opt) => opt.value);
1455
+ }
1456
+ return state;
1457
+ }
1458
+ function isVisible(element, rect, style) {
1459
+ if (rect.width === 0 || rect.height === 0) return false;
1460
+ if (style.display === "none") return false;
1461
+ if (style.visibility === "hidden") return false;
1462
+ if (parseFloat(style.opacity) === 0) return false;
1463
+ return rect.top < window.innerHeight && rect.bottom > 0 && rect.left < window.innerWidth && rect.right > 0;
1464
+ }
1465
+ function isDisabled(element) {
1466
+ if ("disabled" in element && element.disabled) return true;
1467
+ if (element.getAttribute("aria-disabled") === "true") return true;
1468
+ return false;
1469
+ }
1470
+ function sleep(ms) {
1471
+ return new Promise((resolve) => setTimeout(resolve, ms));
1472
+ }
1473
+ function createMouseEvent(type, element, options) {
1474
+ const rect = element.getBoundingClientRect();
1475
+ const x = options?.position?.x ?? rect.width / 2;
1476
+ const y = options?.position?.y ?? rect.height / 2;
1477
+ return new MouseEvent(type, {
1478
+ bubbles: true,
1479
+ cancelable: true,
1480
+ view: window,
1481
+ button: options?.button === "right" ? 2 : options?.button === "middle" ? 1 : 0,
1482
+ clientX: rect.left + x,
1483
+ clientY: rect.top + y
1484
+ });
1485
+ }
1486
+ var DefaultActionExecutor = class {
1487
+ constructor(registry) {
1488
+ this.registry = registry;
1489
+ }
1490
+ /**
1491
+ * Execute an action on an element
1492
+ */
1493
+ async executeAction(elementId, request) {
1494
+ const startTime = performance.now();
1495
+ let waitDurationMs = 0;
1496
+ try {
1497
+ const registered = this.registry.getElement(elementId);
1498
+ let element = registered?.element ?? null;
1499
+ if (!element) {
1500
+ element = findElementByIdentifier(elementId);
1501
+ }
1502
+ if (!element) {
1503
+ return {
1504
+ success: false,
1505
+ error: `Element not found: ${elementId}`,
1506
+ durationMs: performance.now() - startTime,
1507
+ timestamp: Date.now(),
1508
+ requestId: request.requestId
1509
+ };
1510
+ }
1511
+ if (request.waitOptions) {
1512
+ const waitResult = await this.waitForElement(element, request.waitOptions);
1513
+ waitDurationMs = waitResult.waitedMs;
1514
+ if (!waitResult.met) {
1515
+ return {
1516
+ success: false,
1517
+ error: waitResult.error || "Wait condition not met",
1518
+ durationMs: performance.now() - startTime,
1519
+ timestamp: Date.now(),
1520
+ requestId: request.requestId,
1521
+ waitDurationMs
1522
+ };
1523
+ }
1524
+ }
1525
+ const result = await this.performAction(element, request.action, request.params);
1526
+ return {
1527
+ success: true,
1528
+ elementState: getElementState2(element),
1529
+ result,
1530
+ durationMs: performance.now() - startTime,
1531
+ timestamp: Date.now(),
1532
+ requestId: request.requestId,
1533
+ waitDurationMs
1534
+ };
1535
+ } catch (error) {
1536
+ return {
1537
+ success: false,
1538
+ error: error instanceof Error ? error.message : String(error),
1539
+ stack: error instanceof Error ? error.stack : void 0,
1540
+ durationMs: performance.now() - startTime,
1541
+ timestamp: Date.now(),
1542
+ requestId: request.requestId,
1543
+ waitDurationMs
1544
+ };
1545
+ }
1546
+ }
1547
+ /**
1548
+ * Execute an action on a component
1549
+ */
1550
+ async executeComponentAction(componentId, request) {
1551
+ const startTime = performance.now();
1552
+ try {
1553
+ const component = this.registry.getComponent(componentId);
1554
+ if (!component) {
1555
+ return {
1556
+ success: false,
1557
+ error: `Component not found: ${componentId}`,
1558
+ durationMs: performance.now() - startTime,
1559
+ timestamp: Date.now(),
1560
+ requestId: request.requestId
1561
+ };
1562
+ }
1563
+ const action = component.actions.find((a) => a.id === request.action);
1564
+ if (!action) {
1565
+ return {
1566
+ success: false,
1567
+ error: `Action not found: ${request.action}`,
1568
+ durationMs: performance.now() - startTime,
1569
+ timestamp: Date.now(),
1570
+ requestId: request.requestId
1571
+ };
1572
+ }
1573
+ const result = await action.handler(request.params);
1574
+ return {
1575
+ success: true,
1576
+ result,
1577
+ durationMs: performance.now() - startTime,
1578
+ timestamp: Date.now(),
1579
+ requestId: request.requestId
1580
+ };
1581
+ } catch (error) {
1582
+ return {
1583
+ success: false,
1584
+ error: error instanceof Error ? error.message : String(error),
1585
+ stack: error instanceof Error ? error.stack : void 0,
1586
+ durationMs: performance.now() - startTime,
1587
+ timestamp: Date.now(),
1588
+ requestId: request.requestId
1589
+ };
1590
+ }
1591
+ }
1592
+ /**
1593
+ * Wait for a condition on an element
1594
+ */
1595
+ async waitFor(elementId, options) {
1596
+ const registered = this.registry.getElement(elementId);
1597
+ let element = registered?.element ?? null;
1598
+ if (!element) {
1599
+ element = findElementByIdentifier(elementId);
1600
+ }
1601
+ if (!element) {
1602
+ return {
1603
+ met: false,
1604
+ waitedMs: 0,
1605
+ error: `Element not found: ${elementId}`
1606
+ };
1607
+ }
1608
+ return this.waitForElement(element, options);
1609
+ }
1610
+ /**
1611
+ * Find controllable elements
1612
+ */
1613
+ async find(options) {
1614
+ const startTime = performance.now();
1615
+ const elements = [];
1616
+ let root = document.body;
1617
+ if (options?.root) {
1618
+ const rootEl = document.querySelector(options.root);
1619
+ if (rootEl) root = rootEl;
1620
+ }
1621
+ const interactiveSelectors = [
1622
+ "a[href]",
1623
+ "button",
1624
+ "input",
1625
+ "select",
1626
+ "textarea",
1627
+ "[onclick]",
1628
+ '[role="button"]',
1629
+ '[role="link"]',
1630
+ '[role="checkbox"]',
1631
+ '[role="radio"]',
1632
+ '[role="menuitem"]',
1633
+ '[role="tab"]',
1634
+ '[role="switch"]',
1635
+ '[tabindex]:not([tabindex="-1"])',
1636
+ '[contenteditable="true"]',
1637
+ "[data-ui-id]",
1638
+ "[data-testid]"
1639
+ ];
1640
+ const selector = options?.selector || interactiveSelectors.join(", ");
1641
+ const foundElements = root.querySelectorAll(selector);
1642
+ for (const el of foundElements) {
1643
+ if (options?.limit && elements.length >= options.limit) break;
1644
+ const state = getElementState2(el);
1645
+ if (!options?.includeHidden && !state.visible) continue;
1646
+ if (options?.types) {
1647
+ const type = this.inferElementType(el);
1648
+ if (!options.types.includes(type)) continue;
1649
+ }
1650
+ const registered = this.registry.findByDOMElement(el);
1651
+ elements.push({
1652
+ id: registered?.id || this.getElementId(el),
1653
+ type: registered?.type || this.inferElementType(el),
1654
+ label: registered?.label || this.getElementLabel(el),
1655
+ tagName: el.tagName.toLowerCase(),
1656
+ role: el.getAttribute("role") || void 0,
1657
+ accessibleName: this.getAccessibleName(el),
1658
+ actions: registered?.actions || this.inferActions(el),
1659
+ state,
1660
+ registered: !!registered
1661
+ });
1662
+ }
1663
+ return {
1664
+ elements,
1665
+ total: elements.length,
1666
+ durationMs: performance.now() - startTime,
1667
+ timestamp: Date.now()
1668
+ };
1669
+ }
1670
+ /**
1671
+ * Discover controllable elements
1672
+ * @deprecated Use find() instead
1673
+ */
1674
+ async discover(options) {
1675
+ return this.find(options);
1676
+ }
1677
+ /**
1678
+ * Get control snapshot
1679
+ */
1680
+ async getSnapshot() {
1681
+ const elements = this.registry.getAllElements();
1682
+ const components = this.registry.getAllComponents();
1683
+ const workflows = this.registry.getAllWorkflows();
1684
+ return {
1685
+ timestamp: Date.now(),
1686
+ elements: elements.map((el) => ({
1687
+ id: el.id,
1688
+ type: el.type,
1689
+ label: el.label,
1690
+ actions: [...el.actions, ...el.customActions ? Object.keys(el.customActions) : []],
1691
+ state: el.getState()
1692
+ })),
1693
+ components: components.map((comp) => ({
1694
+ id: comp.id,
1695
+ name: comp.name,
1696
+ actions: comp.actions.map((a) => a.id)
1697
+ })),
1698
+ workflows: workflows.map((wf) => ({
1699
+ id: wf.id,
1700
+ name: wf.name,
1701
+ stepCount: wf.steps.length
1702
+ })),
1703
+ activeRuns: []
1704
+ // Workflow engine manages this
1705
+ };
1706
+ }
1707
+ /**
1708
+ * Wait for element conditions
1709
+ */
1710
+ async waitForElement(element, options) {
1711
+ const opts = { ...DEFAULT_WAIT_OPTIONS, ...options };
1712
+ const startTime = performance.now();
1713
+ const deadline = startTime + opts.timeout;
1714
+ while (Date.now() < deadline) {
1715
+ const state = getElementState2(element);
1716
+ let allMet = true;
1717
+ if (opts.visible && !state.visible) allMet = false;
1718
+ if (opts.enabled && !state.enabled) allMet = false;
1719
+ if (opts.focused && !state.focused) allMet = false;
1720
+ if (opts.state) {
1721
+ for (const [key, value] of Object.entries(opts.state)) {
1722
+ if (state[key] !== value) {
1723
+ allMet = false;
1724
+ break;
1725
+ }
1726
+ }
1727
+ }
1728
+ if (allMet) {
1729
+ return {
1730
+ met: true,
1731
+ waitedMs: performance.now() - startTime,
1732
+ state
1733
+ };
1734
+ }
1735
+ await sleep(opts.interval);
1736
+ }
1737
+ return {
1738
+ met: false,
1739
+ waitedMs: performance.now() - startTime,
1740
+ state: getElementState2(element),
1741
+ error: `Timeout waiting for conditions after ${opts.timeout}ms`
1742
+ };
1743
+ }
1744
+ /**
1745
+ * Perform an action on an element
1746
+ */
1747
+ async performAction(element, action, params) {
1748
+ switch (action) {
1749
+ case "click":
1750
+ return this.performClick(element, params);
1751
+ case "doubleClick":
1752
+ return this.performDoubleClick(element, params);
1753
+ case "rightClick":
1754
+ return this.performRightClick(element, params);
1755
+ case "type":
1756
+ return this.performType(element, params);
1757
+ case "clear":
1758
+ return this.performClear(element);
1759
+ case "select":
1760
+ return this.performSelect(element, params);
1761
+ case "focus":
1762
+ return this.performFocus(element);
1763
+ case "blur":
1764
+ return this.performBlur(element);
1765
+ case "hover":
1766
+ return this.performHover(element);
1767
+ case "scroll":
1768
+ return this.performScroll(element, params);
1769
+ case "check":
1770
+ return this.performCheck(element, true);
1771
+ case "uncheck":
1772
+ return this.performCheck(element, false);
1773
+ case "toggle":
1774
+ return this.performToggle(element);
1775
+ default: {
1776
+ const registered = this.registry.findByDOMElement(element);
1777
+ if (registered?.customActions?.[action]) {
1778
+ return registered.customActions[action].handler(params);
1779
+ }
1780
+ throw new Error(`Unknown action: ${action}`);
1781
+ }
1782
+ }
1783
+ }
1784
+ performClick(element, options) {
1785
+ element.dispatchEvent(createMouseEvent("mousedown", element, options));
1786
+ element.dispatchEvent(createMouseEvent("mouseup", element, options));
1787
+ element.dispatchEvent(createMouseEvent("click", element, options));
1788
+ }
1789
+ performDoubleClick(element, options) {
1790
+ this.performClick(element, options);
1791
+ this.performClick(element, options);
1792
+ element.dispatchEvent(createMouseEvent("dblclick", element, options));
1793
+ }
1794
+ performRightClick(element, options) {
1795
+ const opts = { ...options, button: "right" };
1796
+ element.dispatchEvent(createMouseEvent("mousedown", element, opts));
1797
+ element.dispatchEvent(createMouseEvent("mouseup", element, opts));
1798
+ element.dispatchEvent(createMouseEvent("contextmenu", element, opts));
1799
+ }
1800
+ async performType(element, options) {
1801
+ if (!(element instanceof HTMLInputElement || element instanceof HTMLTextAreaElement)) {
1802
+ throw new Error("Type action requires an input or textarea element");
1803
+ }
1804
+ element.focus();
1805
+ if (options?.clear) {
1806
+ element.value = "";
1807
+ element.dispatchEvent(new Event("input", { bubbles: true }));
1808
+ }
1809
+ const text = options?.text || "";
1810
+ const delay = options?.delay || 0;
1811
+ for (const char of text) {
1812
+ element.value += char;
1813
+ if (options?.triggerEvents !== false) {
1814
+ element.dispatchEvent(new Event("input", { bubbles: true }));
1815
+ }
1816
+ if (delay > 0) {
1817
+ await sleep(delay);
1818
+ }
1819
+ }
1820
+ if (options?.triggerEvents !== false) {
1821
+ element.dispatchEvent(new Event("change", { bubbles: true }));
1822
+ }
1823
+ }
1824
+ performClear(element) {
1825
+ if (element instanceof HTMLInputElement || element instanceof HTMLTextAreaElement) {
1826
+ element.value = "";
1827
+ element.dispatchEvent(new Event("input", { bubbles: true }));
1828
+ element.dispatchEvent(new Event("change", { bubbles: true }));
1829
+ }
1830
+ }
1831
+ performSelect(element, options) {
1832
+ if (!(element instanceof HTMLSelectElement)) {
1833
+ throw new Error("Select action requires a select element");
1834
+ }
1835
+ const values = Array.isArray(options?.value) ? options.value : [options?.value];
1836
+ if (!options?.additive) {
1837
+ for (const option of element.options) {
1838
+ option.selected = false;
1839
+ }
1840
+ }
1841
+ for (const option of element.options) {
1842
+ const matchValue = options?.byLabel ? option.text : option.value;
1843
+ if (values.includes(matchValue)) {
1844
+ option.selected = true;
1845
+ }
1846
+ }
1847
+ element.dispatchEvent(new Event("change", { bubbles: true }));
1848
+ }
1849
+ performFocus(element) {
1850
+ element.focus();
1851
+ element.dispatchEvent(new FocusEvent("focus", { bubbles: true }));
1852
+ }
1853
+ performBlur(element) {
1854
+ element.blur();
1855
+ element.dispatchEvent(new FocusEvent("blur", { bubbles: true }));
1856
+ }
1857
+ performHover(element) {
1858
+ element.dispatchEvent(createMouseEvent("mouseenter", element));
1859
+ element.dispatchEvent(createMouseEvent("mouseover", element));
1860
+ }
1861
+ performScroll(element, options) {
1862
+ if (options?.toElement) {
1863
+ const target = document.querySelector(options.toElement);
1864
+ if (target) {
1865
+ target.scrollIntoView({ behavior: options.smooth ? "smooth" : "auto" });
1866
+ return;
1867
+ }
1868
+ }
1869
+ if (options?.position) {
1870
+ element.scrollTo({
1871
+ left: options.position.x,
1872
+ top: options.position.y,
1873
+ behavior: options.smooth ? "smooth" : "auto"
1874
+ });
1875
+ return;
1876
+ }
1877
+ const amount = options?.amount || 100;
1878
+ const direction = options?.direction || "down";
1879
+ switch (direction) {
1880
+ case "up":
1881
+ element.scrollBy({ top: -amount, behavior: options?.smooth ? "smooth" : "auto" });
1882
+ break;
1883
+ case "down":
1884
+ element.scrollBy({ top: amount, behavior: options?.smooth ? "smooth" : "auto" });
1885
+ break;
1886
+ case "left":
1887
+ element.scrollBy({ left: -amount, behavior: options?.smooth ? "smooth" : "auto" });
1888
+ break;
1889
+ case "right":
1890
+ element.scrollBy({ left: amount, behavior: options?.smooth ? "smooth" : "auto" });
1891
+ break;
1892
+ }
1893
+ }
1894
+ performCheck(element, checked) {
1895
+ if (element instanceof HTMLInputElement && (element.type === "checkbox" || element.type === "radio")) {
1896
+ if (element.checked !== checked) {
1897
+ element.checked = checked;
1898
+ element.dispatchEvent(new Event("change", { bubbles: true }));
1899
+ }
1900
+ }
1901
+ }
1902
+ performToggle(element) {
1903
+ if (element instanceof HTMLInputElement && element.type === "checkbox") {
1904
+ element.checked = !element.checked;
1905
+ element.dispatchEvent(new Event("change", { bubbles: true }));
1906
+ }
1907
+ }
1908
+ getElementId(element) {
1909
+ return element.getAttribute("data-ui-id") || element.getAttribute("data-testid") || element.id || `${element.tagName.toLowerCase()}-${Math.random().toString(36).substr(2, 8)}`;
1910
+ }
1911
+ getElementLabel(element) {
1912
+ return element.getAttribute("aria-label") || element.getAttribute("title") || element.textContent?.trim().substring(0, 50) || void 0;
1913
+ }
1914
+ getAccessibleName(element) {
1915
+ const ariaLabel = element.getAttribute("aria-label");
1916
+ if (ariaLabel) return ariaLabel;
1917
+ const labelledBy = element.getAttribute("aria-labelledby");
1918
+ if (labelledBy) {
1919
+ const labels = labelledBy.split(" ").map((id) => document.getElementById(id)?.textContent?.trim()).filter(Boolean);
1920
+ if (labels.length > 0) return labels.join(" ");
1921
+ }
1922
+ if (element instanceof HTMLInputElement || element instanceof HTMLSelectElement || element instanceof HTMLTextAreaElement) {
1923
+ if (element.id) {
1924
+ const label = document.querySelector(`label[for="${element.id}"]`);
1925
+ if (label) return label.textContent?.trim();
1926
+ }
1927
+ }
1928
+ return element.getAttribute("title") || element.textContent?.trim().substring(0, 50) || void 0;
1929
+ }
1930
+ inferElementType(element) {
1931
+ const tagName = element.tagName.toLowerCase();
1932
+ const role = element.getAttribute("role");
1933
+ if (role) {
1934
+ switch (role) {
1935
+ case "button":
1936
+ return "button";
1937
+ case "textbox":
1938
+ return "input";
1939
+ case "checkbox":
1940
+ return "checkbox";
1941
+ case "radio":
1942
+ return "radio";
1943
+ case "link":
1944
+ return "link";
1945
+ case "listbox":
1946
+ case "combobox":
1947
+ return "select";
1948
+ case "menu":
1949
+ return "menu";
1950
+ case "menuitem":
1951
+ return "menuitem";
1952
+ case "tab":
1953
+ return "tab";
1954
+ case "dialog":
1955
+ return "dialog";
1956
+ }
1957
+ }
1958
+ switch (tagName) {
1959
+ case "button":
1960
+ return "button";
1961
+ case "input": {
1962
+ const type = element.type;
1963
+ if (type === "checkbox") return "checkbox";
1964
+ if (type === "radio") return "radio";
1965
+ if (type === "submit" || type === "button") return "button";
1966
+ return "input";
1967
+ }
1968
+ case "textarea":
1969
+ return "textarea";
1970
+ case "select":
1971
+ return "select";
1972
+ case "a":
1973
+ return "link";
1974
+ case "form":
1975
+ return "form";
1976
+ default:
1977
+ return "custom";
1978
+ }
1979
+ }
1980
+ inferActions(element) {
1981
+ const type = this.inferElementType(element);
1982
+ const baseActions = ["focus", "blur", "hover"];
1983
+ switch (type) {
1984
+ case "button":
1985
+ return [...baseActions, "click", "doubleClick", "rightClick"];
1986
+ case "input":
1987
+ return [...baseActions, "click", "type", "clear"];
1988
+ case "textarea":
1989
+ return [...baseActions, "click", "type", "clear"];
1990
+ case "select":
1991
+ return [...baseActions, "click", "select"];
1992
+ case "checkbox":
1993
+ return [...baseActions, "click", "check", "uncheck", "toggle"];
1994
+ case "radio":
1995
+ return [...baseActions, "click", "check"];
1996
+ case "link":
1997
+ return [...baseActions, "click"];
1998
+ default:
1999
+ return [...baseActions, "click"];
2000
+ }
2001
+ }
2002
+ };
2003
+ function createActionExecutor(registry) {
2004
+ return new DefaultActionExecutor(registry);
2005
+ }
2006
+
2007
+ // src/control/workflow-engine.ts
2008
+ function generateRunId() {
2009
+ return `run-${Date.now()}-${Math.random().toString(36).substr(2, 8)}`;
2010
+ }
2011
+ var DefaultWorkflowEngine = class {
2012
+ constructor(registry, executor) {
2013
+ this.registry = registry;
2014
+ this.executor = executor;
2015
+ this.activeRuns = /* @__PURE__ */ new Map();
2016
+ }
2017
+ /**
2018
+ * Run a workflow
2019
+ */
2020
+ async run(workflowId, request) {
2021
+ const workflow = this.registry.getWorkflow(workflowId);
2022
+ if (!workflow) {
2023
+ return {
2024
+ workflowId,
2025
+ runId: generateRunId(),
2026
+ status: "failed",
2027
+ steps: [],
2028
+ totalSteps: 0,
2029
+ success: false,
2030
+ error: `Workflow not found: ${workflowId}`,
2031
+ startedAt: Date.now(),
2032
+ completedAt: Date.now(),
2033
+ durationMs: 0
2034
+ };
2035
+ }
2036
+ const runId = generateRunId();
2037
+ const state = {
2038
+ workflowId,
2039
+ runId,
2040
+ workflow,
2041
+ request,
2042
+ status: "running",
2043
+ steps: [],
2044
+ currentStep: 0,
2045
+ startedAt: Date.now()
2046
+ };
2047
+ this.activeRuns.set(runId, state);
2048
+ try {
2049
+ await this.executeWorkflow(state);
2050
+ } catch (error) {
2051
+ state.status = "failed";
2052
+ state.error = error instanceof Error ? error.message : String(error);
2053
+ }
2054
+ state.completedAt = Date.now();
2055
+ state.durationMs = state.completedAt - state.startedAt;
2056
+ state.success = state.status === "completed" && state.steps.every((s) => s.success);
2057
+ setTimeout(() => {
2058
+ this.activeRuns.delete(runId);
2059
+ }, 6e4);
2060
+ return this.buildResponse(state);
2061
+ }
2062
+ /**
2063
+ * Get workflow run status
2064
+ */
2065
+ async getRunStatus(runId) {
2066
+ const state = this.activeRuns.get(runId);
2067
+ if (!state) return null;
2068
+ return this.buildResponse(state);
2069
+ }
2070
+ /**
2071
+ * Cancel a running workflow
2072
+ */
2073
+ async cancel(runId) {
2074
+ const state = this.activeRuns.get(runId);
2075
+ if (!state || state.status !== "running") return false;
2076
+ state.status = "cancelled";
2077
+ state.completedAt = Date.now();
2078
+ state.durationMs = state.completedAt - state.startedAt;
2079
+ state.error = "Workflow cancelled by user";
2080
+ return true;
2081
+ }
2082
+ /**
2083
+ * List active runs
2084
+ */
2085
+ async listActiveRuns() {
2086
+ return Array.from(this.activeRuns.values()).filter((state) => state.status === "running").map((state) => this.buildResponse(state));
2087
+ }
2088
+ /**
2089
+ * Execute a workflow
2090
+ */
2091
+ async executeWorkflow(state) {
2092
+ const { workflow, request } = state;
2093
+ const params = { ...workflow.defaultParams, ...request?.params };
2094
+ let startIndex = 0;
2095
+ if (request?.startStep) {
2096
+ const idx = workflow.steps.findIndex((s) => s.id === request.startStep);
2097
+ if (idx >= 0) startIndex = idx;
2098
+ }
2099
+ let stopIndex = workflow.steps.length;
2100
+ if (request?.stopStep) {
2101
+ const idx = workflow.steps.findIndex((s) => s.id === request.stopStep);
2102
+ if (idx >= 0) stopIndex = idx + 1;
2103
+ }
2104
+ for (let i = startIndex; i < stopIndex; i++) {
2105
+ if (state.status === "cancelled") break;
2106
+ state.currentStep = i;
2107
+ const step = workflow.steps[i];
2108
+ const stepResult = await this.executeStep(step, params, request?.stepTimeout);
2109
+ state.steps.push(stepResult);
2110
+ if (!stepResult.success) {
2111
+ state.status = "failed";
2112
+ state.error = stepResult.error;
2113
+ return;
2114
+ }
2115
+ }
2116
+ state.status = "completed";
2117
+ }
2118
+ /**
2119
+ * Execute a single step
2120
+ */
2121
+ async executeStep(step, params, timeout) {
2122
+ const startTime = performance.now();
2123
+ try {
2124
+ const timeoutPromise = timeout ? new Promise(
2125
+ (_, reject) => setTimeout(() => reject(new Error("Step timeout")), timeout)
2126
+ ) : null;
2127
+ const executePromise = this.executeStepInternal(step, params);
2128
+ const result = timeoutPromise ? await Promise.race([executePromise, timeoutPromise]) : await executePromise;
2129
+ return {
2130
+ stepId: step.id,
2131
+ stepType: step.type,
2132
+ success: true,
2133
+ result,
2134
+ durationMs: performance.now() - startTime,
2135
+ timestamp: Date.now()
2136
+ };
2137
+ } catch (error) {
2138
+ return {
2139
+ stepId: step.id,
2140
+ stepType: step.type,
2141
+ success: false,
2142
+ error: error instanceof Error ? error.message : String(error),
2143
+ durationMs: performance.now() - startTime,
2144
+ timestamp: Date.now()
2145
+ };
2146
+ }
2147
+ }
2148
+ /**
2149
+ * Execute step internal logic
2150
+ */
2151
+ async executeStepInternal(step, params) {
2152
+ const resolvedParams = this.interpolateParams(step.params || {}, params);
2153
+ switch (step.type) {
2154
+ case "element-action":
2155
+ if (!step.target || !step.action) {
2156
+ throw new Error("Element action requires target and action");
2157
+ }
2158
+ return this.executor.executeAction(step.target, {
2159
+ action: step.action,
2160
+ params: resolvedParams,
2161
+ waitOptions: step.waitOptions
2162
+ });
2163
+ case "component-action":
2164
+ if (!step.target || !step.action) {
2165
+ throw new Error("Component action requires target and action");
2166
+ }
2167
+ return this.executor.executeComponentAction(step.target, {
2168
+ action: step.action,
2169
+ params: resolvedParams
2170
+ });
2171
+ case "wait": {
2172
+ if (!step.target) {
2173
+ throw new Error("Wait step requires target");
2174
+ }
2175
+ const waitResult = await this.executor.waitFor(step.target, step.waitOptions || {});
2176
+ if (!waitResult.met) {
2177
+ throw new Error(waitResult.error || "Wait condition not met");
2178
+ }
2179
+ return waitResult;
2180
+ }
2181
+ case "assert":
2182
+ if (!step.target || !step.expectedState) {
2183
+ throw new Error("Assert step requires target and expectedState");
2184
+ }
2185
+ return this.performAssertion(step.target, step.expectedState);
2186
+ case "custom":
2187
+ if (!step.handler) {
2188
+ throw new Error("Custom step requires handler");
2189
+ }
2190
+ return step.handler();
2191
+ default:
2192
+ throw new Error(`Unknown step type: ${step.type}`);
2193
+ }
2194
+ }
2195
+ /**
2196
+ * Perform state assertion
2197
+ */
2198
+ async performAssertion(target, expectedState) {
2199
+ const snapshot = await this.executor.getSnapshot();
2200
+ const element = snapshot.elements.find((e) => e.id === target);
2201
+ if (!element) {
2202
+ throw new Error(`Element not found for assertion: ${target}`);
2203
+ }
2204
+ const differences = [];
2205
+ for (const [key, expected] of Object.entries(expectedState)) {
2206
+ const actual = element.state[key];
2207
+ if (actual !== expected) {
2208
+ differences.push(`${key}: expected ${expected}, got ${actual}`);
2209
+ }
2210
+ }
2211
+ if (differences.length > 0) {
2212
+ throw new Error(`Assertion failed:
2213
+ ${differences.join("\n")}`);
2214
+ }
2215
+ return { passed: true, differences };
2216
+ }
2217
+ /**
2218
+ * Interpolate parameters with {{param}} syntax
2219
+ */
2220
+ interpolateParams(stepParams, workflowParams) {
2221
+ const result = {};
2222
+ for (const [key, value] of Object.entries(stepParams)) {
2223
+ if (typeof value === "string") {
2224
+ result[key] = value.replace(/\{\{(\w+)\}\}/g, (_, name) => {
2225
+ return String(workflowParams[name] ?? "");
2226
+ });
2227
+ } else {
2228
+ result[key] = value;
2229
+ }
2230
+ }
2231
+ return result;
2232
+ }
2233
+ /**
2234
+ * Build response from state
2235
+ */
2236
+ buildResponse(state) {
2237
+ return {
2238
+ workflowId: state.workflowId,
2239
+ runId: state.runId,
2240
+ status: state.status,
2241
+ steps: [...state.steps],
2242
+ currentStep: state.currentStep,
2243
+ totalSteps: state.workflow.steps.length,
2244
+ success: state.success,
2245
+ error: state.error,
2246
+ startedAt: state.startedAt,
2247
+ completedAt: state.completedAt,
2248
+ durationMs: state.durationMs
2249
+ };
2250
+ }
2251
+ };
2252
+ function createWorkflowEngine(registry, executor) {
2253
+ return new DefaultWorkflowEngine(registry, executor);
2254
+ }
2255
+
2256
+ // src/render-log/dom-capture.ts
2257
+ var CAPTURE_ATTRIBUTES = [
2258
+ "data-ui-id",
2259
+ "data-testid",
2260
+ "data-awas-element",
2261
+ "id",
2262
+ "name",
2263
+ "type",
2264
+ "href",
2265
+ "src",
2266
+ "alt",
2267
+ "title",
2268
+ "placeholder",
2269
+ "value",
2270
+ "aria-label",
2271
+ "aria-labelledby",
2272
+ "aria-describedby",
2273
+ "aria-expanded",
2274
+ "aria-selected",
2275
+ "aria-checked",
2276
+ "aria-disabled",
2277
+ "aria-hidden",
2278
+ "role",
2279
+ "tabindex",
2280
+ "disabled",
2281
+ "readonly",
2282
+ "required",
2283
+ "checked"
2284
+ ];
2285
+ var INTERACTIVE_SELECTORS = [
2286
+ "a[href]",
2287
+ "button",
2288
+ "input",
2289
+ "select",
2290
+ "textarea",
2291
+ "[onclick]",
2292
+ '[role="button"]',
2293
+ '[role="link"]',
2294
+ '[role="checkbox"]',
2295
+ '[role="radio"]',
2296
+ '[role="menuitem"]',
2297
+ '[role="tab"]',
2298
+ '[role="switch"]',
2299
+ '[tabindex]:not([tabindex="-1"])',
2300
+ '[contenteditable="true"]'
2301
+ ];
2302
+ function isInteractive(element) {
2303
+ return INTERACTIVE_SELECTORS.some((selector) => {
2304
+ try {
2305
+ return element.matches(selector);
2306
+ } catch {
2307
+ return false;
2308
+ }
2309
+ });
2310
+ }
2311
+ function getAccessibleName(element) {
2312
+ const ariaLabel = element.getAttribute("aria-label");
2313
+ if (ariaLabel) return ariaLabel;
2314
+ const labelledBy = element.getAttribute("aria-labelledby");
2315
+ if (labelledBy) {
2316
+ const labels = labelledBy.split(" ").map((id) => document.getElementById(id)?.textContent?.trim()).filter(Boolean);
2317
+ if (labels.length > 0) return labels.join(" ");
2318
+ }
2319
+ if (element instanceof HTMLInputElement || element instanceof HTMLSelectElement || element instanceof HTMLTextAreaElement) {
2320
+ const id = element.id;
2321
+ if (id) {
2322
+ const label = document.querySelector(`label[for="${id}"]`);
2323
+ if (label) return label.textContent?.trim();
2324
+ }
2325
+ }
2326
+ const title = element.getAttribute("title");
2327
+ if (title) return title;
2328
+ if (element instanceof HTMLImageElement) {
2329
+ return element.alt || void 0;
2330
+ }
2331
+ if (element.matches('button, a, [role="button"], [role="link"]')) {
2332
+ return element.textContent?.trim() || void 0;
2333
+ }
2334
+ return void 0;
2335
+ }
2336
+ function getElementState3(element) {
2337
+ const rect = element.getBoundingClientRect();
2338
+ const style = window.getComputedStyle(element);
2339
+ const state = {
2340
+ visible: isVisible2(element, rect, style),
2341
+ enabled: !isDisabled2(element),
2342
+ focused: document.activeElement === element,
2343
+ rect: {
2344
+ x: rect.x,
2345
+ y: rect.y,
2346
+ width: rect.width,
2347
+ height: rect.height,
2348
+ top: rect.top,
2349
+ right: rect.right,
2350
+ bottom: rect.bottom,
2351
+ left: rect.left
2352
+ },
2353
+ computedStyles: {
2354
+ display: style.display,
2355
+ visibility: style.visibility,
2356
+ opacity: style.opacity,
2357
+ pointerEvents: style.pointerEvents
2358
+ }
2359
+ };
2360
+ if (element instanceof HTMLInputElement) {
2361
+ state.value = element.value;
2362
+ if (element.type === "checkbox" || element.type === "radio") {
2363
+ state.checked = element.checked;
2364
+ }
2365
+ } else if (element instanceof HTMLTextAreaElement) {
2366
+ state.value = element.value;
2367
+ } else if (element instanceof HTMLSelectElement) {
2368
+ state.value = element.value;
2369
+ state.selectedOptions = Array.from(element.selectedOptions).map((opt) => opt.value);
2370
+ }
2371
+ return state;
2372
+ }
2373
+ function isVisible2(element, rect, style) {
2374
+ if (rect.width === 0 || rect.height === 0) return false;
2375
+ if (style.display === "none") return false;
2376
+ if (style.visibility === "hidden") return false;
2377
+ if (parseFloat(style.opacity) === 0) return false;
2378
+ if (element.getAttribute("aria-hidden") === "true") return false;
2379
+ return rect.top < window.innerHeight && rect.bottom > 0 && rect.left < window.innerWidth && rect.right > 0;
2380
+ }
2381
+ function isDisabled2(element) {
2382
+ if ("disabled" in element && element.disabled) return true;
2383
+ if (element.getAttribute("aria-disabled") === "true") return true;
2384
+ return false;
2385
+ }
2386
+ function captureAttributes(element) {
2387
+ const attrs = {};
2388
+ for (const attr of CAPTURE_ATTRIBUTES) {
2389
+ const value = element.getAttribute(attr);
2390
+ if (value !== null) {
2391
+ attrs[attr] = value;
2392
+ }
2393
+ }
2394
+ return attrs;
2395
+ }
2396
+ function captureElement(element, depth, maxTextLength) {
2397
+ const identifier = createElementIdentifier(element);
2398
+ let textContent = element.textContent?.trim();
2399
+ if (textContent && textContent.length > maxTextLength) {
2400
+ textContent = textContent.substring(0, maxTextLength) + "...";
2401
+ }
2402
+ return {
2403
+ identifier,
2404
+ bestId: getBestIdentifier(element),
2405
+ tagName: element.tagName.toLowerCase(),
2406
+ role: element.getAttribute("role") || void 0,
2407
+ accessibleName: getAccessibleName(element),
2408
+ textContent,
2409
+ state: getElementState3(element),
2410
+ attributes: captureAttributes(element),
2411
+ childCount: element.children.length,
2412
+ depth
2413
+ };
2414
+ }
2415
+ function captureDOMSnapshot(options = {}) {
2416
+ const startTime = performance.now();
2417
+ const {
2418
+ root = document.body,
2419
+ maxDepth = 50,
2420
+ maxElements = 5e3,
2421
+ interactiveOnly = false,
2422
+ includeHidden = false,
2423
+ includeSelectors,
2424
+ excludeSelectors,
2425
+ filter,
2426
+ maxTextLength = 200
2427
+ } = options;
2428
+ const elements = [];
2429
+ let totalNodeCount = 0;
2430
+ function shouldCapture(element) {
2431
+ if (filter && !filter(element)) return false;
2432
+ if (excludeSelectors) {
2433
+ for (const selector of excludeSelectors) {
2434
+ try {
2435
+ if (element.matches(selector)) return false;
2436
+ } catch {
2437
+ }
2438
+ }
2439
+ }
2440
+ if (includeSelectors && includeSelectors.length > 0) {
2441
+ let matches = false;
2442
+ for (const selector of includeSelectors) {
2443
+ try {
2444
+ if (element.matches(selector)) {
2445
+ matches = true;
2446
+ break;
2447
+ }
2448
+ } catch {
2449
+ }
2450
+ }
2451
+ if (!matches) return false;
2452
+ }
2453
+ if (interactiveOnly && !isInteractive(element)) return false;
2454
+ if (!includeHidden) {
2455
+ const rect = element.getBoundingClientRect();
2456
+ const style = window.getComputedStyle(element);
2457
+ if (!isVisible2(element, rect, style)) return false;
2458
+ }
2459
+ return true;
2460
+ }
2461
+ function traverse(element, depth) {
2462
+ if (depth > maxDepth || elements.length >= maxElements) return;
2463
+ totalNodeCount++;
2464
+ if (shouldCapture(element)) {
2465
+ elements.push(captureElement(element, depth, maxTextLength));
2466
+ }
2467
+ for (const child of element.children) {
2468
+ if (child instanceof HTMLElement) {
2469
+ traverse(child, depth + 1);
2470
+ }
2471
+ }
2472
+ }
2473
+ traverse(root, 0);
2474
+ const endTime = performance.now();
2475
+ return {
2476
+ timestamp: Date.now(),
2477
+ url: window.location.href,
2478
+ title: document.title,
2479
+ viewport: {
2480
+ width: window.innerWidth,
2481
+ height: window.innerHeight,
2482
+ scrollX: window.scrollX,
2483
+ scrollY: window.scrollY
2484
+ },
2485
+ elements,
2486
+ totalNodeCount,
2487
+ captureDurationMs: endTime - startTime
2488
+ };
2489
+ }
2490
+ function captureInteractiveElements(options = {}) {
2491
+ return captureDOMSnapshot({ ...options, interactiveOnly: true });
2492
+ }
2493
+ var DOMChangeObserver = class {
2494
+ constructor(options = {}) {
2495
+ this.observer = null;
2496
+ this.changes = [];
2497
+ this.maxChanges = options.maxChanges ?? 1e3;
2498
+ this.callback = options.callback;
2499
+ }
2500
+ start(root = document.body) {
2501
+ if (this.observer) return;
2502
+ this.observer = new MutationObserver((mutations) => {
2503
+ for (const mutation of mutations) {
2504
+ const change = this.processMutation(mutation);
2505
+ if (change) {
2506
+ this.addChange(change);
2507
+ }
2508
+ }
2509
+ });
2510
+ this.observer.observe(root, {
2511
+ childList: true,
2512
+ attributes: true,
2513
+ characterData: true,
2514
+ subtree: true,
2515
+ attributeOldValue: true
2516
+ });
2517
+ }
2518
+ stop() {
2519
+ this.observer?.disconnect();
2520
+ this.observer = null;
2521
+ }
2522
+ processMutation(mutation) {
2523
+ const target = mutation.target;
2524
+ if (!(target instanceof HTMLElement)) return null;
2525
+ const elementId = getBestIdentifier(target);
2526
+ if (mutation.type === "attributes") {
2527
+ return {
2528
+ timestamp: Date.now(),
2529
+ type: "attribute",
2530
+ elementId,
2531
+ tagName: target.tagName.toLowerCase(),
2532
+ details: {
2533
+ attributeName: mutation.attributeName || void 0,
2534
+ oldValue: mutation.oldValue || void 0,
2535
+ newValue: mutation.attributeName ? target.getAttribute(mutation.attributeName) || void 0 : void 0
2536
+ }
2537
+ };
2538
+ }
2539
+ if (mutation.type === "childList") {
2540
+ if (mutation.addedNodes.length > 0) {
2541
+ return {
2542
+ timestamp: Date.now(),
2543
+ type: "added",
2544
+ elementId,
2545
+ tagName: target.tagName.toLowerCase(),
2546
+ details: {
2547
+ addedNodes: mutation.addedNodes.length
2548
+ }
2549
+ };
2550
+ }
2551
+ if (mutation.removedNodes.length > 0) {
2552
+ return {
2553
+ timestamp: Date.now(),
2554
+ type: "removed",
2555
+ elementId,
2556
+ tagName: target.tagName.toLowerCase(),
2557
+ details: {
2558
+ removedNodes: mutation.removedNodes.length
2559
+ }
2560
+ };
2561
+ }
2562
+ }
2563
+ return null;
2564
+ }
2565
+ addChange(change) {
2566
+ this.changes.push(change);
2567
+ if (this.changes.length > this.maxChanges) {
2568
+ this.changes.shift();
2569
+ }
2570
+ this.callback?.(change);
2571
+ }
2572
+ getChanges() {
2573
+ return [...this.changes];
2574
+ }
2575
+ clearChanges() {
2576
+ this.changes = [];
2577
+ }
2578
+ };
2579
+
2580
+ // src/render-log/snapshot.ts
2581
+ var InMemoryRenderLogStorage = class {
2582
+ constructor(maxEntries = 1e3) {
2583
+ this.entries = [];
2584
+ this.maxEntries = maxEntries;
2585
+ }
2586
+ async append(entry) {
2587
+ this.entries.push(entry);
2588
+ while (this.entries.length > this.maxEntries) {
2589
+ this.entries.shift();
2590
+ }
2591
+ }
2592
+ async getEntries(options) {
2593
+ let results = [...this.entries];
2594
+ if (options?.type) {
2595
+ results = results.filter((e) => e.type === options.type);
2596
+ }
2597
+ if (options?.since) {
2598
+ results = results.filter((e) => e.timestamp >= options.since);
2599
+ }
2600
+ if (options?.until) {
2601
+ results = results.filter((e) => e.timestamp <= options.until);
2602
+ }
2603
+ if (options?.limit) {
2604
+ results = results.slice(-options.limit);
2605
+ }
2606
+ return results;
2607
+ }
2608
+ async clear() {
2609
+ this.entries = [];
2610
+ }
2611
+ async count() {
2612
+ return this.entries.length;
2613
+ }
2614
+ /** Get entries synchronously (for in-memory only) */
2615
+ getEntriesSync() {
2616
+ return [...this.entries];
2617
+ }
2618
+ };
2619
+ function generateId2() {
2620
+ return `${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
2621
+ }
2622
+ var RenderLogManager = class {
2623
+ constructor(options = {}) {
2624
+ this.changeObserver = null;
2625
+ this.snapshotTimer = null;
2626
+ this.pendingChanges = [];
2627
+ this.started = false;
2628
+ this.options = options;
2629
+ this.storage = options.storage ?? new InMemoryRenderLogStorage(options.maxEntries);
2630
+ }
2631
+ /**
2632
+ * Start capturing
2633
+ */
2634
+ start() {
2635
+ if (this.started) return;
2636
+ this.started = true;
2637
+ if (this.options.captureChanges !== false) {
2638
+ this.changeObserver = new DOMChangeObserver({
2639
+ callback: (change) => {
2640
+ this.pendingChanges.push(change);
2641
+ }
2642
+ });
2643
+ this.changeObserver.start();
2644
+ }
2645
+ if (this.options.captureOnNavigation !== false) {
2646
+ this.setupNavigationObserver();
2647
+ }
2648
+ if (this.options.snapshotInterval) {
2649
+ this.snapshotTimer = setInterval(() => {
2650
+ this.captureSnapshot();
2651
+ }, this.options.snapshotInterval);
2652
+ }
2653
+ this.captureSnapshot();
2654
+ }
2655
+ /**
2656
+ * Stop capturing
2657
+ */
2658
+ stop() {
2659
+ if (!this.started) return;
2660
+ this.started = false;
2661
+ this.changeObserver?.stop();
2662
+ this.changeObserver = null;
2663
+ if (this.snapshotTimer) {
2664
+ clearInterval(this.snapshotTimer);
2665
+ this.snapshotTimer = null;
2666
+ }
2667
+ }
2668
+ /**
2669
+ * Capture a DOM snapshot
2670
+ */
2671
+ async captureSnapshot(metadata) {
2672
+ if (this.pendingChanges.length > 0) {
2673
+ await this.flushChanges();
2674
+ }
2675
+ const snapshot = captureDOMSnapshot(this.options.captureOptions);
2676
+ const entry = {
2677
+ id: generateId2(),
2678
+ type: "snapshot",
2679
+ timestamp: snapshot.timestamp,
2680
+ data: snapshot,
2681
+ metadata
2682
+ };
2683
+ await this.addEntry(entry);
2684
+ return entry;
2685
+ }
2686
+ /**
2687
+ * Flush pending DOM changes
2688
+ */
2689
+ async flushChanges() {
2690
+ if (this.pendingChanges.length === 0) return null;
2691
+ const changes = [...this.pendingChanges];
2692
+ this.pendingChanges = [];
2693
+ const entry = {
2694
+ id: generateId2(),
2695
+ type: "change",
2696
+ timestamp: Date.now(),
2697
+ data: changes
2698
+ };
2699
+ await this.addEntry(entry);
2700
+ return entry;
2701
+ }
2702
+ /**
2703
+ * Log an interaction
2704
+ */
2705
+ async logInteraction(eventType, details) {
2706
+ const entry = {
2707
+ id: generateId2(),
2708
+ type: "interaction",
2709
+ timestamp: Date.now(),
2710
+ data: {
2711
+ eventType,
2712
+ ...details
2713
+ }
2714
+ };
2715
+ await this.addEntry(entry);
2716
+ return entry;
2717
+ }
2718
+ /**
2719
+ * Log an error
2720
+ */
2721
+ async logError(message, details) {
2722
+ const entry = {
2723
+ id: generateId2(),
2724
+ type: "error",
2725
+ timestamp: Date.now(),
2726
+ data: {
2727
+ message,
2728
+ ...details
2729
+ }
2730
+ };
2731
+ await this.addEntry(entry);
2732
+ return entry;
2733
+ }
2734
+ /**
2735
+ * Log a navigation
2736
+ */
2737
+ async logNavigation(from, to, navigationType) {
2738
+ const entry = {
2739
+ id: generateId2(),
2740
+ type: "navigation",
2741
+ timestamp: Date.now(),
2742
+ data: {
2743
+ from,
2744
+ to,
2745
+ navigationType
2746
+ }
2747
+ };
2748
+ await this.addEntry(entry);
2749
+ return entry;
2750
+ }
2751
+ /**
2752
+ * Add a custom entry
2753
+ */
2754
+ async logCustom(data, metadata) {
2755
+ const entry = {
2756
+ id: generateId2(),
2757
+ type: "custom",
2758
+ timestamp: Date.now(),
2759
+ data,
2760
+ metadata
2761
+ };
2762
+ await this.addEntry(entry);
2763
+ return entry;
2764
+ }
2765
+ /**
2766
+ * Get log entries
2767
+ */
2768
+ async getEntries(options) {
2769
+ return this.storage.getEntries(options);
2770
+ }
2771
+ /**
2772
+ * Clear the log
2773
+ */
2774
+ async clear() {
2775
+ this.pendingChanges = [];
2776
+ await this.storage.clear();
2777
+ }
2778
+ /**
2779
+ * Get entry count
2780
+ */
2781
+ async count() {
2782
+ return this.storage.count();
2783
+ }
2784
+ /**
2785
+ * Get the latest snapshot
2786
+ */
2787
+ async getLatestSnapshot() {
2788
+ const snapshots = await this.storage.getEntries({ type: "snapshot", limit: 1 });
2789
+ return snapshots[0] || null;
2790
+ }
2791
+ async addEntry(entry) {
2792
+ await this.storage.append(entry);
2793
+ this.options.onEntry?.(entry);
2794
+ }
2795
+ setupNavigationObserver() {
2796
+ let lastUrl = window.location.href;
2797
+ const originalPushState = history.pushState;
2798
+ const originalReplaceState = history.replaceState;
2799
+ history.pushState = (...args) => {
2800
+ const result = originalPushState.apply(history, args);
2801
+ const newUrl = window.location.href;
2802
+ if (newUrl !== lastUrl) {
2803
+ this.logNavigation(lastUrl, newUrl, "push");
2804
+ this.captureSnapshot({ trigger: "navigation" });
2805
+ lastUrl = newUrl;
2806
+ }
2807
+ return result;
2808
+ };
2809
+ history.replaceState = (...args) => {
2810
+ const result = originalReplaceState.apply(history, args);
2811
+ const newUrl = window.location.href;
2812
+ if (newUrl !== lastUrl) {
2813
+ this.logNavigation(lastUrl, newUrl, "replace");
2814
+ this.captureSnapshot({ trigger: "navigation" });
2815
+ lastUrl = newUrl;
2816
+ }
2817
+ return result;
2818
+ };
2819
+ window.addEventListener("popstate", () => {
2820
+ const newUrl = window.location.href;
2821
+ if (newUrl !== lastUrl) {
2822
+ this.logNavigation(lastUrl, newUrl, "pop");
2823
+ this.captureSnapshot({ trigger: "navigation" });
2824
+ lastUrl = newUrl;
2825
+ }
2826
+ });
2827
+ }
2828
+ };
2829
+ function createRenderLogManager(options) {
2830
+ return new RenderLogManager(options);
2831
+ }
2832
+
2833
+ // src/debug/metrics.ts
2834
+ function generateId3() {
2835
+ return `${Date.now()}-${Math.random().toString(36).substr(2, 8)}`;
2836
+ }
2837
+ var MetricsCollector = class {
2838
+ constructor(options = {}) {
2839
+ this.history = [];
2840
+ this.maxHistoryEntries = options.maxHistoryEntries ?? 1e3;
2841
+ this.rateWindow = options.rateWindow ?? 6e4;
2842
+ }
2843
+ /**
2844
+ * Record an element action
2845
+ */
2846
+ recordElementAction(target, action, response, params) {
2847
+ const entry = {
2848
+ id: generateId3(),
2849
+ timestamp: response.timestamp,
2850
+ type: "element",
2851
+ target,
2852
+ action,
2853
+ success: response.success,
2854
+ durationMs: response.durationMs,
2855
+ error: response.error,
2856
+ params,
2857
+ response: response.elementState
2858
+ };
2859
+ this.addEntry(entry);
2860
+ return entry;
2861
+ }
2862
+ /**
2863
+ * Record a component action
2864
+ */
2865
+ recordComponentAction(target, action, response, params) {
2866
+ const entry = {
2867
+ id: generateId3(),
2868
+ timestamp: response.timestamp,
2869
+ type: "component",
2870
+ target,
2871
+ action,
2872
+ success: response.success,
2873
+ durationMs: response.durationMs,
2874
+ error: response.error,
2875
+ params,
2876
+ response: response.result
2877
+ };
2878
+ this.addEntry(entry);
2879
+ return entry;
2880
+ }
2881
+ /**
2882
+ * Record a workflow step
2883
+ */
2884
+ recordWorkflowStep(workflowId, result) {
2885
+ const entry = {
2886
+ id: generateId3(),
2887
+ timestamp: result.timestamp,
2888
+ type: "workflow-step",
2889
+ target: workflowId,
2890
+ action: result.stepId,
2891
+ success: result.success,
2892
+ durationMs: result.durationMs,
2893
+ error: result.error,
2894
+ response: result.result
2895
+ };
2896
+ this.addEntry(entry);
2897
+ return entry;
2898
+ }
2899
+ /**
2900
+ * Record from a bridge event
2901
+ */
2902
+ recordEvent(event) {
2903
+ if (event.type === "action:completed" || event.type === "action:failed") {
2904
+ const data = event.data;
2905
+ if (data.elementId) {
2906
+ this.recordElementAction(
2907
+ data.elementId,
2908
+ data.action,
2909
+ data.response,
2910
+ data.params
2911
+ );
2912
+ } else if (data.componentId) {
2913
+ this.recordComponentAction(
2914
+ data.componentId,
2915
+ data.action,
2916
+ data.response,
2917
+ data.params
2918
+ );
2919
+ }
2920
+ }
2921
+ }
2922
+ /**
2923
+ * Get action history
2924
+ */
2925
+ getHistory(options) {
2926
+ let results = [...this.history];
2927
+ if (options?.type) {
2928
+ results = results.filter((e) => e.type === options.type);
2929
+ }
2930
+ if (options?.target) {
2931
+ results = results.filter((e) => e.target === options.target);
2932
+ }
2933
+ if (options?.action) {
2934
+ results = results.filter((e) => e.action === options.action);
2935
+ }
2936
+ if (options?.success !== void 0) {
2937
+ results = results.filter((e) => e.success === options.success);
2938
+ }
2939
+ if (options?.since) {
2940
+ results = results.filter((e) => e.timestamp >= options.since);
2941
+ }
2942
+ if (options?.limit) {
2943
+ results = results.slice(-options.limit);
2944
+ }
2945
+ return results;
2946
+ }
2947
+ /**
2948
+ * Get performance metrics
2949
+ */
2950
+ getMetrics(since) {
2951
+ const entries = since ? this.history.filter((e) => e.timestamp >= since) : this.history;
2952
+ if (entries.length === 0) {
2953
+ return {
2954
+ totalActions: 0,
2955
+ successfulActions: 0,
2956
+ failedActions: 0,
2957
+ successRate: 0,
2958
+ avgDurationMs: 0,
2959
+ minDurationMs: 0,
2960
+ maxDurationMs: 0,
2961
+ p95DurationMs: 0,
2962
+ actionsPerSecond: 0,
2963
+ errorsByType: {},
2964
+ actionsByType: {}
2965
+ };
2966
+ }
2967
+ const successful = entries.filter((e) => e.success);
2968
+ const failed = entries.filter((e) => !e.success);
2969
+ const durations = entries.map((e) => e.durationMs).sort((a, b) => a - b);
2970
+ const now = Date.now();
2971
+ const windowStart = now - this.rateWindow;
2972
+ const recentActions = this.history.filter((e) => e.timestamp >= windowStart);
2973
+ const windowSeconds = this.rateWindow / 1e3;
2974
+ const errorsByType = {};
2975
+ for (const entry of failed) {
2976
+ const errorType = entry.error?.split(":")[0] || "Unknown";
2977
+ errorsByType[errorType] = (errorsByType[errorType] || 0) + 1;
2978
+ }
2979
+ const actionsByType = {};
2980
+ for (const entry of entries) {
2981
+ const key = `${entry.type}:${entry.action}`;
2982
+ actionsByType[key] = (actionsByType[key] || 0) + 1;
2983
+ }
2984
+ return {
2985
+ totalActions: entries.length,
2986
+ successfulActions: successful.length,
2987
+ failedActions: failed.length,
2988
+ successRate: successful.length / entries.length,
2989
+ avgDurationMs: durations.reduce((a, b) => a + b, 0) / durations.length,
2990
+ minDurationMs: durations[0],
2991
+ maxDurationMs: durations[durations.length - 1],
2992
+ p95DurationMs: durations[Math.floor(durations.length * 0.95)],
2993
+ actionsPerSecond: recentActions.length / windowSeconds,
2994
+ errorsByType,
2995
+ actionsByType
2996
+ };
2997
+ }
2998
+ /**
2999
+ * Get recent errors
3000
+ */
3001
+ getRecentErrors(limit = 10) {
3002
+ return this.history.filter((e) => !e.success).slice(-limit);
3003
+ }
3004
+ /**
3005
+ * Get slowest actions
3006
+ */
3007
+ getSlowestActions(limit = 10) {
3008
+ return [...this.history].sort((a, b) => b.durationMs - a.durationMs).slice(0, limit);
3009
+ }
3010
+ /**
3011
+ * Clear history
3012
+ */
3013
+ clearHistory() {
3014
+ this.history = [];
3015
+ }
3016
+ /**
3017
+ * Export history as JSON
3018
+ */
3019
+ exportHistory() {
3020
+ return JSON.stringify(this.history, null, 2);
3021
+ }
3022
+ /**
3023
+ * Import history from JSON
3024
+ */
3025
+ importHistory(json) {
3026
+ const entries = JSON.parse(json);
3027
+ this.history = entries.slice(-this.maxHistoryEntries);
3028
+ }
3029
+ addEntry(entry) {
3030
+ this.history.push(entry);
3031
+ while (this.history.length > this.maxHistoryEntries) {
3032
+ this.history.shift();
3033
+ }
3034
+ }
3035
+ };
3036
+ function createMetricsCollector(options) {
3037
+ return new MetricsCollector(options);
3038
+ }
3039
+ function formatDuration(ms) {
3040
+ if (ms < 1) return "<1ms";
3041
+ if (ms < 1e3) return `${Math.round(ms)}ms`;
3042
+ if (ms < 6e4) return `${(ms / 1e3).toFixed(1)}s`;
3043
+ return `${(ms / 6e4).toFixed(1)}m`;
3044
+ }
3045
+ function formatPercentage(value) {
3046
+ return `${(value * 100).toFixed(1)}%`;
3047
+ }
3048
+ var UIBridgeContext = react.createContext(null);
3049
+ function UIBridgeProvider({
3050
+ children,
3051
+ features = {},
3052
+ config = {},
3053
+ onEvent
3054
+ }) {
3055
+ const registryRef = react.useRef(null);
3056
+ const renderLogRef = react.useRef(null);
3057
+ const metricsRef = react.useRef(null);
3058
+ const wsClientRef = react.useRef(null);
3059
+ const [wsConnectionState, setWsConnectionState] = react.useState("disconnected");
3060
+ if (!registryRef.current) {
3061
+ registryRef.current = new UIBridgeRegistry({
3062
+ verbose: config.verbose,
3063
+ onEvent
3064
+ });
3065
+ setGlobalRegistry(registryRef.current);
3066
+ if (features.renderLog) {
3067
+ renderLogRef.current = createRenderLogManager({
3068
+ maxEntries: config.maxLogEntries
3069
+ });
3070
+ }
3071
+ if (features.debug) {
3072
+ metricsRef.current = createMetricsCollector();
3073
+ }
3074
+ if (config.websocket) {
3075
+ const wsPort = config.websocketPort || config.serverPort || 9876;
3076
+ const wsUrl = `ws://localhost:${wsPort}`;
3077
+ wsClientRef.current = createWSClient({
3078
+ url: wsUrl,
3079
+ autoReconnect: true,
3080
+ reconnectDelay: 1e3,
3081
+ maxReconnectAttempts: 10,
3082
+ pingInterval: 3e4
3083
+ });
3084
+ }
3085
+ }
3086
+ const registry = registryRef.current;
3087
+ const renderLog = renderLogRef.current || void 0;
3088
+ const metrics = metricsRef.current || void 0;
3089
+ const wsClient = wsClientRef.current || void 0;
3090
+ const executor = react.useMemo(() => createActionExecutor(registry), [registry]);
3091
+ const workflowEngine = react.useMemo(
3092
+ () => createWorkflowEngine(registry, executor),
3093
+ [registry, executor]
3094
+ );
3095
+ react.useEffect(() => {
3096
+ if (features.renderLog && renderLog) {
3097
+ renderLog.start();
3098
+ return () => renderLog.stop();
3099
+ }
3100
+ }, [features.renderLog, renderLog]);
3101
+ react.useEffect(() => {
3102
+ if (!metrics) return;
3103
+ const unsubCompleted = registry.on("action:completed", (event) => {
3104
+ metrics.recordEvent(event);
3105
+ });
3106
+ const unsubFailed = registry.on("action:failed", (event) => {
3107
+ metrics.recordEvent(event);
3108
+ });
3109
+ return () => {
3110
+ unsubCompleted();
3111
+ unsubFailed();
3112
+ };
3113
+ }, [registry, metrics]);
3114
+ react.useEffect(() => {
3115
+ if (!wsClient) return;
3116
+ const unsubscribe = wsClient.onConnectionChange((state) => {
3117
+ setWsConnectionState(state);
3118
+ });
3119
+ return unsubscribe;
3120
+ }, [wsClient]);
3121
+ react.useEffect(() => {
3122
+ return () => {
3123
+ renderLog?.stop();
3124
+ wsClient?.disconnect();
3125
+ resetGlobalRegistry();
3126
+ };
3127
+ }, [renderLog, wsClient]);
3128
+ const wsConnect = react.useCallback(async () => {
3129
+ if (wsClient) {
3130
+ await wsClient.connect();
3131
+ }
3132
+ }, [wsClient]);
3133
+ const wsDisconnect = react.useCallback(() => {
3134
+ wsClient?.disconnect();
3135
+ }, [wsClient]);
3136
+ const wsSubscribe = react.useCallback(
3137
+ async (options) => {
3138
+ if (!wsClient) {
3139
+ return [];
3140
+ }
3141
+ return wsClient.subscribe(options);
3142
+ },
3143
+ [wsClient]
3144
+ );
3145
+ const onWsEvent = react.useCallback(
3146
+ (eventType, listener) => {
3147
+ if (!wsClient) {
3148
+ return () => {
3149
+ };
3150
+ }
3151
+ return wsClient.onEvent(eventType, listener);
3152
+ },
3153
+ [wsClient]
3154
+ );
3155
+ const getElements = react.useCallback(() => registry.getAllElements(), [registry]);
3156
+ const getComponents = react.useCallback(() => registry.getAllComponents(), [registry]);
3157
+ const createSnapshot = react.useCallback(() => registry.createSnapshot(), [registry]);
3158
+ const on = react.useCallback(
3159
+ (type, listener) => registry.on(type, listener),
3160
+ [registry]
3161
+ );
3162
+ const off = react.useCallback(
3163
+ (type, listener) => registry.off(type, listener),
3164
+ [registry]
3165
+ );
3166
+ const contextValue = react.useMemo(
3167
+ () => ({
3168
+ features,
3169
+ config,
3170
+ registry,
3171
+ executor,
3172
+ workflowEngine,
3173
+ renderLog,
3174
+ metrics,
3175
+ wsClient,
3176
+ wsConnectionState,
3177
+ getElements,
3178
+ getComponents,
3179
+ createSnapshot,
3180
+ on,
3181
+ off,
3182
+ initialized: true,
3183
+ wsConnect,
3184
+ wsDisconnect,
3185
+ wsSubscribe,
3186
+ onWsEvent
3187
+ }),
3188
+ [
3189
+ features,
3190
+ config,
3191
+ registry,
3192
+ executor,
3193
+ workflowEngine,
3194
+ renderLog,
3195
+ metrics,
3196
+ wsClient,
3197
+ wsConnectionState,
3198
+ getElements,
3199
+ getComponents,
3200
+ createSnapshot,
3201
+ on,
3202
+ off,
3203
+ wsConnect,
3204
+ wsDisconnect,
3205
+ wsSubscribe,
3206
+ onWsEvent
3207
+ ]
3208
+ );
3209
+ return /* @__PURE__ */ jsxRuntime.jsx(UIBridgeContext.Provider, { value: contextValue, children });
3210
+ }
3211
+ function useUIBridgeContext() {
3212
+ const context = react.useContext(UIBridgeContext);
3213
+ if (!context) {
3214
+ throw new Error("useUIBridgeContext must be used within a UIBridgeProvider");
3215
+ }
3216
+ return context;
3217
+ }
3218
+ function useUIBridgeOptional() {
3219
+ return react.useContext(UIBridgeContext);
3220
+ }
3221
+ function useUIElement(options) {
3222
+ const bridge = useUIBridgeOptional();
3223
+ const elementRef = react.useRef(null);
3224
+ const registeredRef = react.useRef(false);
3225
+ const { id, type, label, actions, customActions, autoRegister = true } = options;
3226
+ const register = react.useCallback(() => {
3227
+ if (!bridge || !elementRef.current || registeredRef.current) return;
3228
+ bridge.registry.registerElement(id, elementRef.current, {
3229
+ type,
3230
+ label,
3231
+ actions,
3232
+ customActions
3233
+ });
3234
+ registeredRef.current = true;
3235
+ }, [bridge, id, type, label, actions, customActions]);
3236
+ const unregister = react.useCallback(() => {
3237
+ if (!bridge || !registeredRef.current) return;
3238
+ bridge.registry.unregisterElement(id);
3239
+ registeredRef.current = false;
3240
+ }, [bridge, id]);
3241
+ const ref = react.useCallback(
3242
+ (node) => {
3243
+ if (elementRef.current && elementRef.current !== node) {
3244
+ unregister();
3245
+ }
3246
+ elementRef.current = node;
3247
+ if (node && autoRegister) {
3248
+ register();
3249
+ }
3250
+ },
3251
+ [autoRegister, register, unregister]
3252
+ );
3253
+ react.useEffect(() => {
3254
+ return () => {
3255
+ unregister();
3256
+ };
3257
+ }, [unregister]);
3258
+ const getState = react.useCallback(() => {
3259
+ if (!bridge) return null;
3260
+ const registered = bridge.registry.getElement(id);
3261
+ return registered?.getState() || null;
3262
+ }, [bridge, id]);
3263
+ const getIdentifier = react.useCallback(() => {
3264
+ if (!bridge) return null;
3265
+ const registered = bridge.registry.getElement(id);
3266
+ return registered?.getIdentifier() || null;
3267
+ }, [bridge, id]);
3268
+ const trigger = react.useCallback(
3269
+ async (action, params) => {
3270
+ if (!bridge) {
3271
+ throw new Error("UI Bridge not available");
3272
+ }
3273
+ const response = await bridge.executor.executeAction(id, {
3274
+ action,
3275
+ params
3276
+ });
3277
+ if (!response.success) {
3278
+ throw new Error(response.error || "Action failed");
3279
+ }
3280
+ },
3281
+ [bridge, id]
3282
+ );
3283
+ const registeredElement = react.useMemo(() => {
3284
+ if (!bridge) return null;
3285
+ return bridge.registry.getElement(id) || null;
3286
+ }, [bridge, id]);
3287
+ return {
3288
+ ref,
3289
+ element: elementRef.current,
3290
+ registered: registeredRef.current,
3291
+ getState,
3292
+ getIdentifier,
3293
+ trigger,
3294
+ register,
3295
+ unregister,
3296
+ registeredElement
3297
+ };
3298
+ }
3299
+ function useUIElementRef(id) {
3300
+ return react.useCallback(
3301
+ (node) => {
3302
+ if (node) {
3303
+ node.setAttribute("data-ui-id", id);
3304
+ }
3305
+ },
3306
+ [id]
3307
+ );
3308
+ }
3309
+ function useUIComponent(options) {
3310
+ const bridge = useUIBridgeOptional();
3311
+ const registeredRef = react.useRef(false);
3312
+ const actionsRef = react.useRef(options.actions || []);
3313
+ const elementIdsRef = react.useRef(options.elementIds || []);
3314
+ const { id, name, description, autoRegister = true } = options;
3315
+ react.useEffect(() => {
3316
+ actionsRef.current = options.actions || [];
3317
+ elementIdsRef.current = options.elementIds || [];
3318
+ }, [options.actions, options.elementIds]);
3319
+ const register = react.useCallback(() => {
3320
+ if (!bridge || registeredRef.current) return;
3321
+ bridge.registry.registerComponent(id, {
3322
+ name,
3323
+ description,
3324
+ actions: actionsRef.current.map((a) => ({
3325
+ id: a.id,
3326
+ label: a.label,
3327
+ description: a.description,
3328
+ handler: a.handler
3329
+ })),
3330
+ elementIds: elementIdsRef.current
3331
+ });
3332
+ registeredRef.current = true;
3333
+ }, [bridge, id, name, description]);
3334
+ const unregister = react.useCallback(() => {
3335
+ if (!bridge || !registeredRef.current) return;
3336
+ bridge.registry.unregisterComponent(id);
3337
+ registeredRef.current = false;
3338
+ }, [bridge, id]);
3339
+ const executeAction = react.useCallback(
3340
+ async (actionId, params) => {
3341
+ if (!bridge) {
3342
+ throw new Error("UI Bridge not available");
3343
+ }
3344
+ const response = await bridge.executor.executeComponentAction(id, {
3345
+ action: actionId,
3346
+ params
3347
+ });
3348
+ if (!response.success) {
3349
+ throw new Error(response.error || "Action failed");
3350
+ }
3351
+ return response.result;
3352
+ },
3353
+ [bridge, id]
3354
+ );
3355
+ const updateActions = react.useCallback(
3356
+ (actions) => {
3357
+ actionsRef.current = actions;
3358
+ if (registeredRef.current && bridge) {
3359
+ bridge.registry.unregisterComponent(id);
3360
+ registeredRef.current = false;
3361
+ register();
3362
+ }
3363
+ },
3364
+ [bridge, id, register]
3365
+ );
3366
+ const addElement = react.useCallback((elementId) => {
3367
+ if (!elementIdsRef.current.includes(elementId)) {
3368
+ elementIdsRef.current = [...elementIdsRef.current, elementId];
3369
+ }
3370
+ }, []);
3371
+ const removeElement = react.useCallback((elementId) => {
3372
+ elementIdsRef.current = elementIdsRef.current.filter((id2) => id2 !== elementId);
3373
+ }, []);
3374
+ react.useEffect(() => {
3375
+ if (autoRegister) {
3376
+ register();
3377
+ }
3378
+ return () => {
3379
+ unregister();
3380
+ };
3381
+ }, [autoRegister, register, unregister]);
3382
+ const registeredComponent = react.useMemo(() => {
3383
+ if (!bridge) return null;
3384
+ return bridge.registry.getComponent(id) || null;
3385
+ }, [bridge, id]);
3386
+ return {
3387
+ registered: registeredRef.current,
3388
+ executeAction,
3389
+ register,
3390
+ unregister,
3391
+ updateActions,
3392
+ addElement,
3393
+ removeElement,
3394
+ registeredComponent
3395
+ };
3396
+ }
3397
+ function useUIComponentAction(handler, deps) {
3398
+ return react.useCallback(handler, deps);
3399
+ }
3400
+ function useUIBridge() {
3401
+ const context = useUIBridgeOptional();
3402
+ const available = !!context;
3403
+ const initialized = context?.initialized ?? false;
3404
+ const elements = react.useMemo(() => context?.getElements() ?? [], [context]);
3405
+ const components = react.useMemo(() => context?.getComponents() ?? [], [context]);
3406
+ const workflows = react.useMemo(() => context?.registry.getAllWorkflows() ?? [], [context]);
3407
+ const createSnapshot = react.useCallback(() => {
3408
+ if (!context) {
3409
+ return {
3410
+ timestamp: Date.now(),
3411
+ elements: [],
3412
+ components: [],
3413
+ workflows: []
3414
+ };
3415
+ }
3416
+ return context.createSnapshot();
3417
+ }, [context]);
3418
+ const executeAction = react.useCallback(
3419
+ async (elementId, request) => {
3420
+ if (!context) {
3421
+ return {
3422
+ success: false,
3423
+ error: "UI Bridge not available",
3424
+ durationMs: 0,
3425
+ timestamp: Date.now()
3426
+ };
3427
+ }
3428
+ return context.executor.executeAction(elementId, request);
3429
+ },
3430
+ [context]
3431
+ );
3432
+ const executeComponentAction = react.useCallback(
3433
+ async (componentId, request) => {
3434
+ if (!context) {
3435
+ return {
3436
+ success: false,
3437
+ error: "UI Bridge not available",
3438
+ durationMs: 0,
3439
+ timestamp: Date.now()
3440
+ };
3441
+ }
3442
+ return context.executor.executeComponentAction(componentId, request);
3443
+ },
3444
+ [context]
3445
+ );
3446
+ const find = react.useCallback(
3447
+ async (options) => {
3448
+ if (!context) {
3449
+ return {
3450
+ elements: [],
3451
+ total: 0,
3452
+ durationMs: 0,
3453
+ timestamp: Date.now()
3454
+ };
3455
+ }
3456
+ return context.executor.find(options);
3457
+ },
3458
+ [context]
3459
+ );
3460
+ const discover = react.useCallback(
3461
+ async (options) => {
3462
+ return find(options);
3463
+ },
3464
+ [find]
3465
+ );
3466
+ const runWorkflow = react.useCallback(
3467
+ async (workflowId, request) => {
3468
+ if (!context) {
3469
+ return {
3470
+ workflowId,
3471
+ runId: "",
3472
+ status: "failed",
3473
+ steps: [],
3474
+ totalSteps: 0,
3475
+ success: false,
3476
+ error: "UI Bridge not available",
3477
+ startedAt: Date.now(),
3478
+ completedAt: Date.now(),
3479
+ durationMs: 0
3480
+ };
3481
+ }
3482
+ return context.workflowEngine.run(workflowId, request);
3483
+ },
3484
+ [context]
3485
+ );
3486
+ const getElement = react.useCallback(
3487
+ (id) => {
3488
+ return context?.registry.getElement(id);
3489
+ },
3490
+ [context]
3491
+ );
3492
+ const getComponent = react.useCallback(
3493
+ (id) => {
3494
+ return context?.registry.getComponent(id);
3495
+ },
3496
+ [context]
3497
+ );
3498
+ const getElementState5 = react.useCallback(
3499
+ (id) => {
3500
+ const element = context?.registry.getElement(id);
3501
+ return element?.getState();
3502
+ },
3503
+ [context]
3504
+ );
3505
+ const registerWorkflow = react.useCallback(
3506
+ (workflow) => {
3507
+ context?.registry.registerWorkflow(workflow);
3508
+ },
3509
+ [context]
3510
+ );
3511
+ const unregisterWorkflow = react.useCallback(
3512
+ (id) => {
3513
+ context?.registry.unregisterWorkflow(id);
3514
+ },
3515
+ [context]
3516
+ );
3517
+ const captureRenderLog = react.useCallback(async () => {
3518
+ await context?.renderLog?.captureSnapshot();
3519
+ }, [context]);
3520
+ const getRenderLogEntries = react.useCallback(async () => {
3521
+ return await context?.renderLog?.getEntries() ?? [];
3522
+ }, [context]);
3523
+ const clearRenderLog = react.useCallback(async () => {
3524
+ await context?.renderLog?.clear();
3525
+ }, [context]);
3526
+ const getMetrics = react.useCallback(() => {
3527
+ return context?.metrics?.getMetrics();
3528
+ }, [context]);
3529
+ const getActionHistory = react.useCallback(() => {
3530
+ return context?.metrics?.getHistory();
3531
+ }, [context]);
3532
+ return {
3533
+ available,
3534
+ initialized,
3535
+ elements,
3536
+ components,
3537
+ workflows,
3538
+ createSnapshot,
3539
+ executeAction,
3540
+ executeComponentAction,
3541
+ find,
3542
+ discover,
3543
+ // deprecated - use find
3544
+ runWorkflow,
3545
+ getElement,
3546
+ getComponent,
3547
+ getElementState: getElementState5,
3548
+ registerWorkflow,
3549
+ unregisterWorkflow,
3550
+ captureRenderLog,
3551
+ getRenderLogEntries,
3552
+ clearRenderLog,
3553
+ getMetrics,
3554
+ getActionHistory
3555
+ };
3556
+ }
3557
+ function useUIBridgeRequired() {
3558
+ useUIBridgeContext();
3559
+ return useUIBridge();
3560
+ }
3561
+ function useUIState(options) {
3562
+ const bridge = useUIBridgeOptional();
3563
+ const [registered, setRegistered] = react.useState(false);
3564
+ const [isActive, setIsActive] = react.useState(options.initialActive ?? false);
3565
+ const [activeStates, setActiveStates] = react.useState([]);
3566
+ const {
3567
+ id,
3568
+ name,
3569
+ elements = [],
3570
+ activeWhen,
3571
+ blocking,
3572
+ blocks,
3573
+ group,
3574
+ pathCost,
3575
+ metadata,
3576
+ autoRegister = true,
3577
+ initialActive = false
3578
+ } = options;
3579
+ const register = react.useCallback(() => {
3580
+ if (!bridge || registered) return;
3581
+ const state2 = {
3582
+ id,
3583
+ name,
3584
+ elements,
3585
+ activeWhen,
3586
+ blocking,
3587
+ blocks,
3588
+ group,
3589
+ pathCost,
3590
+ metadata
3591
+ };
3592
+ bridge.registry.registerState(state2);
3593
+ setRegistered(true);
3594
+ if (initialActive) {
3595
+ bridge.registry.activateState(id);
3596
+ setIsActive(true);
3597
+ }
3598
+ setActiveStates(bridge.registry.getActiveStates());
3599
+ }, [
3600
+ bridge,
3601
+ registered,
3602
+ id,
3603
+ name,
3604
+ elements,
3605
+ activeWhen,
3606
+ blocking,
3607
+ blocks,
3608
+ group,
3609
+ pathCost,
3610
+ metadata,
3611
+ initialActive
3612
+ ]);
3613
+ const unregister = react.useCallback(() => {
3614
+ if (!bridge || !registered) return;
3615
+ bridge.registry.unregisterState(id);
3616
+ setRegistered(false);
3617
+ setIsActive(false);
3618
+ }, [bridge, registered, id]);
3619
+ react.useEffect(() => {
3620
+ if (autoRegister && bridge) {
3621
+ register();
3622
+ }
3623
+ return () => {
3624
+ if (registered) {
3625
+ unregister();
3626
+ }
3627
+ };
3628
+ }, [autoRegister, bridge, register, unregister, registered]);
3629
+ react.useEffect(() => {
3630
+ if (!bridge) return;
3631
+ const unsubscribe = bridge.registry.on("element:stateChanged", (event) => {
3632
+ const data = event.data;
3633
+ if (data.stateId === id) {
3634
+ setIsActive(data.active);
3635
+ }
3636
+ setActiveStates(data.activeStates);
3637
+ });
3638
+ return unsubscribe;
3639
+ }, [bridge, id]);
3640
+ const activate = react.useCallback(() => {
3641
+ if (!bridge) return false;
3642
+ const success = bridge.registry.activateState(id);
3643
+ if (success) {
3644
+ setIsActive(true);
3645
+ setActiveStates(bridge.registry.getActiveStates());
3646
+ }
3647
+ return success;
3648
+ }, [bridge, id]);
3649
+ const deactivate = react.useCallback(() => {
3650
+ if (!bridge) return false;
3651
+ const success = bridge.registry.deactivateState(id);
3652
+ if (success) {
3653
+ setIsActive(false);
3654
+ setActiveStates(bridge.registry.getActiveStates());
3655
+ }
3656
+ return success;
3657
+ }, [bridge, id]);
3658
+ const toggle = react.useCallback(() => {
3659
+ return isActive ? deactivate() : activate();
3660
+ }, [isActive, activate, deactivate]);
3661
+ const state = react.useMemo(() => {
3662
+ if (!bridge) return void 0;
3663
+ return bridge.registry.getState(id);
3664
+ }, [bridge, id, registered]);
3665
+ return {
3666
+ registered,
3667
+ isActive,
3668
+ activate,
3669
+ deactivate,
3670
+ toggle,
3671
+ activeStates,
3672
+ register,
3673
+ unregister,
3674
+ state
3675
+ };
3676
+ }
3677
+ function useUIStateGroup(options) {
3678
+ const bridge = useUIBridgeOptional();
3679
+ const [registered, setRegistered] = react.useState(false);
3680
+ const { id, name, states, autoRegister = true } = options;
3681
+ const register = react.useCallback(() => {
3682
+ if (!bridge || registered) return;
3683
+ const group2 = { id, name, states };
3684
+ bridge.registry.registerStateGroup(group2);
3685
+ setRegistered(true);
3686
+ }, [bridge, registered, id, name, states]);
3687
+ const unregister = react.useCallback(() => {
3688
+ if (!bridge || !registered) return;
3689
+ bridge.registry.unregisterStateGroup(id);
3690
+ setRegistered(false);
3691
+ }, [bridge, registered, id]);
3692
+ react.useEffect(() => {
3693
+ if (autoRegister && bridge) {
3694
+ register();
3695
+ }
3696
+ return () => {
3697
+ if (registered) {
3698
+ unregister();
3699
+ }
3700
+ };
3701
+ }, [autoRegister, bridge, register, unregister, registered]);
3702
+ const activate = react.useCallback(() => {
3703
+ if (!bridge) return [];
3704
+ return bridge.registry.activateStateGroup(id);
3705
+ }, [bridge, id]);
3706
+ const deactivate = react.useCallback(() => {
3707
+ if (!bridge) return [];
3708
+ return bridge.registry.deactivateStateGroup(id);
3709
+ }, [bridge, id]);
3710
+ const group = react.useMemo(() => {
3711
+ if (!bridge) return void 0;
3712
+ return bridge.registry.getStateGroup(id);
3713
+ }, [bridge, id, registered]);
3714
+ return {
3715
+ registered,
3716
+ activate,
3717
+ deactivate,
3718
+ register,
3719
+ unregister,
3720
+ group
3721
+ };
3722
+ }
3723
+ function useActiveStates() {
3724
+ const bridge = useUIBridgeOptional();
3725
+ const [activeStates, setActiveStates] = react.useState([]);
3726
+ react.useEffect(() => {
3727
+ if (!bridge) return;
3728
+ setActiveStates(bridge.registry.getActiveStates());
3729
+ const unsubscribe = bridge.registry.on("element:stateChanged", (event) => {
3730
+ const data = event.data;
3731
+ setActiveStates(data.activeStates);
3732
+ });
3733
+ return unsubscribe;
3734
+ }, [bridge]);
3735
+ return activeStates;
3736
+ }
3737
+ function useStateSnapshot() {
3738
+ const bridge = useUIBridgeOptional();
3739
+ return react.useMemo(() => {
3740
+ if (!bridge) return null;
3741
+ return bridge.registry.createStateSnapshot();
3742
+ }, [bridge]);
3743
+ }
3744
+ function useUITransition(options) {
3745
+ const bridge = useUIBridgeOptional();
3746
+ const [registered, setRegistered] = react.useState(false);
3747
+ const [canExecute, setCanExecute] = react.useState(false);
3748
+ const {
3749
+ id,
3750
+ name,
3751
+ fromStates,
3752
+ activateStates,
3753
+ exitStates,
3754
+ activateGroups,
3755
+ exitGroups,
3756
+ actions,
3757
+ pathCost,
3758
+ staysVisible,
3759
+ autoRegister = true
3760
+ } = options;
3761
+ const register = react.useCallback(() => {
3762
+ if (!bridge || registered) return;
3763
+ const transition2 = {
3764
+ id,
3765
+ name,
3766
+ fromStates,
3767
+ activateStates,
3768
+ exitStates,
3769
+ activateGroups,
3770
+ exitGroups,
3771
+ actions,
3772
+ pathCost,
3773
+ staysVisible
3774
+ };
3775
+ bridge.registry.registerTransition(transition2);
3776
+ setRegistered(true);
3777
+ setCanExecute(bridge.registry.canExecuteTransition(id));
3778
+ }, [
3779
+ bridge,
3780
+ registered,
3781
+ id,
3782
+ name,
3783
+ fromStates,
3784
+ activateStates,
3785
+ exitStates,
3786
+ activateGroups,
3787
+ exitGroups,
3788
+ actions,
3789
+ pathCost,
3790
+ staysVisible
3791
+ ]);
3792
+ const unregister = react.useCallback(() => {
3793
+ if (!bridge || !registered) return;
3794
+ bridge.registry.unregisterTransition(id);
3795
+ setRegistered(false);
3796
+ setCanExecute(false);
3797
+ }, [bridge, registered, id]);
3798
+ react.useEffect(() => {
3799
+ if (autoRegister && bridge) {
3800
+ register();
3801
+ }
3802
+ return () => {
3803
+ if (registered) {
3804
+ unregister();
3805
+ }
3806
+ };
3807
+ }, [autoRegister, bridge, register, unregister, registered]);
3808
+ react.useEffect(() => {
3809
+ if (!bridge || !registered) return;
3810
+ const unsubscribe = bridge.registry.on("element:stateChanged", () => {
3811
+ setCanExecute(bridge.registry.canExecuteTransition(id));
3812
+ });
3813
+ return unsubscribe;
3814
+ }, [bridge, id, registered]);
3815
+ const execute = react.useCallback(async () => {
3816
+ if (!bridge) {
3817
+ return {
3818
+ success: false,
3819
+ activatedStates: [],
3820
+ deactivatedStates: [],
3821
+ error: "UI Bridge not available",
3822
+ durationMs: 0
3823
+ };
3824
+ }
3825
+ const result = await bridge.registry.executeTransition(id);
3826
+ setCanExecute(bridge.registry.canExecuteTransition(id));
3827
+ return result;
3828
+ }, [bridge, id]);
3829
+ const transition = react.useMemo(() => {
3830
+ if (!bridge) return void 0;
3831
+ return bridge.registry.getTransition(id);
3832
+ }, [bridge, id, registered]);
3833
+ return {
3834
+ registered,
3835
+ canExecute,
3836
+ execute,
3837
+ register,
3838
+ unregister,
3839
+ transition
3840
+ };
3841
+ }
3842
+ function useTransitions() {
3843
+ const bridge = useUIBridgeOptional();
3844
+ return react.useMemo(() => {
3845
+ if (!bridge) return [];
3846
+ return bridge.registry.getAllTransitions();
3847
+ }, [bridge]);
3848
+ }
3849
+ function useAvailableTransitions() {
3850
+ const bridge = useUIBridgeOptional();
3851
+ const [available, setAvailable] = react.useState([]);
3852
+ react.useEffect(() => {
3853
+ if (!bridge) return;
3854
+ const updateAvailable = () => {
3855
+ const transitions = bridge.registry.getAllTransitions();
3856
+ const availableTransitions = transitions.filter(
3857
+ (t) => bridge.registry.canExecuteTransition(t.id)
3858
+ );
3859
+ setAvailable(availableTransitions);
3860
+ };
3861
+ updateAvailable();
3862
+ const unsubscribe = bridge.registry.on("element:stateChanged", updateAvailable);
3863
+ return unsubscribe;
3864
+ }, [bridge]);
3865
+ return available;
3866
+ }
3867
+ function useUINavigation() {
3868
+ const bridge = useUIBridgeOptional();
3869
+ const [isNavigating, setIsNavigating] = react.useState(false);
3870
+ const [lastResult, setLastResult] = react.useState(null);
3871
+ const available = !!bridge;
3872
+ const activeStates = react.useMemo(() => {
3873
+ if (!bridge) return [];
3874
+ return bridge.registry.getActiveStates();
3875
+ }, [bridge]);
3876
+ const findPath = react.useCallback(
3877
+ (targetStates) => {
3878
+ if (!bridge) {
3879
+ return {
3880
+ found: false,
3881
+ transitions: [],
3882
+ totalCost: 0,
3883
+ targetStates,
3884
+ estimatedSteps: 0
3885
+ };
3886
+ }
3887
+ return bridge.registry.findPath(targetStates);
3888
+ },
3889
+ [bridge]
3890
+ );
3891
+ const navigateTo = react.useCallback(
3892
+ async (targetStates) => {
3893
+ if (!bridge) {
3894
+ const result = {
3895
+ success: false,
3896
+ path: {
3897
+ found: false,
3898
+ transitions: [],
3899
+ totalCost: 0,
3900
+ targetStates,
3901
+ estimatedSteps: 0
3902
+ },
3903
+ executedTransitions: [],
3904
+ finalActiveStates: [],
3905
+ error: "UI Bridge not available",
3906
+ durationMs: 0
3907
+ };
3908
+ setLastResult(result);
3909
+ return result;
3910
+ }
3911
+ setIsNavigating(true);
3912
+ try {
3913
+ const result = await bridge.registry.navigateTo(targetStates);
3914
+ setLastResult(result);
3915
+ return result;
3916
+ } finally {
3917
+ setIsNavigating(false);
3918
+ }
3919
+ },
3920
+ [bridge]
3921
+ );
3922
+ return {
3923
+ available,
3924
+ isNavigating,
3925
+ lastResult,
3926
+ findPath,
3927
+ navigateTo,
3928
+ activeStates
3929
+ };
3930
+ }
3931
+ function useCanNavigateTo(targetStates) {
3932
+ const bridge = useUIBridgeOptional();
3933
+ return react.useMemo(() => {
3934
+ if (!bridge) return false;
3935
+ const path = bridge.registry.findPath(targetStates);
3936
+ return path.found;
3937
+ }, [bridge, targetStates]);
3938
+ }
3939
+ function useNavigationPath(targetStates) {
3940
+ const bridge = useUIBridgeOptional();
3941
+ return react.useMemo(() => {
3942
+ if (!bridge) {
3943
+ return {
3944
+ found: false,
3945
+ transitions: [],
3946
+ totalCost: 0,
3947
+ targetStates,
3948
+ estimatedSteps: 0
3949
+ };
3950
+ }
3951
+ return bridge.registry.findPath(targetStates);
3952
+ }, [bridge, targetStates]);
3953
+ }
3954
+ var INTERACTIVE_SELECTORS2 = [
3955
+ "a[href]",
3956
+ "button",
3957
+ "input",
3958
+ "select",
3959
+ "textarea",
3960
+ '[role="button"]',
3961
+ '[role="link"]',
3962
+ '[role="checkbox"]',
3963
+ '[role="radio"]',
3964
+ '[role="menuitem"]',
3965
+ '[role="tab"]',
3966
+ '[role="switch"]',
3967
+ '[role="slider"]',
3968
+ '[role="spinbutton"]',
3969
+ '[role="combobox"]',
3970
+ '[role="listbox"]',
3971
+ '[role="option"]',
3972
+ '[role="textbox"]',
3973
+ '[tabindex]:not([tabindex="-1"])',
3974
+ '[contenteditable="true"]',
3975
+ "[data-ui-element]",
3976
+ // Explicitly marked for registration
3977
+ "[data-testid]"
3978
+ // Testing library convention
3979
+ ];
3980
+ function inferElementType2(element) {
3981
+ const role = element.getAttribute("role");
3982
+ if (role) {
3983
+ const roleMap = {
3984
+ button: "button",
3985
+ link: "link",
3986
+ checkbox: "checkbox",
3987
+ radio: "radio",
3988
+ menuitem: "menuitem",
3989
+ tab: "tab",
3990
+ switch: "switch",
3991
+ slider: "slider",
3992
+ combobox: "combobox",
3993
+ listbox: "listbox",
3994
+ option: "option",
3995
+ textbox: "textbox"
3996
+ };
3997
+ if (role in roleMap) {
3998
+ return roleMap[role];
3999
+ }
4000
+ }
4001
+ const tagName = element.tagName.toLowerCase();
4002
+ switch (tagName) {
4003
+ case "a":
4004
+ return "link";
4005
+ case "button":
4006
+ return "button";
4007
+ case "input": {
4008
+ const type = element.type?.toLowerCase() || "text";
4009
+ switch (type) {
4010
+ case "checkbox":
4011
+ return "checkbox";
4012
+ case "radio":
4013
+ return "radio";
4014
+ case "range":
4015
+ return "slider";
4016
+ case "submit":
4017
+ case "button":
4018
+ return "button";
4019
+ default:
4020
+ return "input";
4021
+ }
4022
+ }
4023
+ case "select":
4024
+ return "select";
4025
+ case "textarea":
4026
+ return "textarea";
4027
+ case "option":
4028
+ return "option";
4029
+ default:
4030
+ return "generic";
4031
+ }
4032
+ }
4033
+ function inferActions2(type) {
4034
+ const baseActions = ["focus", "blur"];
4035
+ const typeActions = {
4036
+ button: [...baseActions, "click", "hover"],
4037
+ link: [...baseActions, "click", "hover"],
4038
+ input: [...baseActions, "type", "clear", "click"],
4039
+ textarea: [...baseActions, "type", "clear", "click"],
4040
+ textbox: [...baseActions, "type", "clear", "click"],
4041
+ checkbox: [...baseActions, "check", "uncheck", "toggle", "click"],
4042
+ radio: [...baseActions, "click", "select"],
4043
+ select: [...baseActions, "select", "click"],
4044
+ combobox: [...baseActions, "select", "type", "click"],
4045
+ listbox: [...baseActions, "select", "click"],
4046
+ option: [...baseActions, "select", "click"],
4047
+ switch: [...baseActions, "toggle", "click"],
4048
+ slider: [...baseActions, "setValue", "click", "drag"],
4049
+ tab: [...baseActions, "click", "select"],
4050
+ menuitem: [...baseActions, "click"],
4051
+ dialog: [...baseActions],
4052
+ menu: [...baseActions],
4053
+ form: [...baseActions, "submit", "reset"],
4054
+ custom: [...baseActions, "click"],
4055
+ generic: [...baseActions, "click"]
4056
+ };
4057
+ return typeActions[type] || baseActions;
4058
+ }
4059
+ function getAccessibleLabel(element) {
4060
+ const ariaLabel = element.getAttribute("aria-label");
4061
+ if (ariaLabel) return ariaLabel;
4062
+ const labelledBy = element.getAttribute("aria-labelledby");
4063
+ if (labelledBy) {
4064
+ const labelEl = document.getElementById(labelledBy);
4065
+ if (labelEl) return labelEl.textContent?.trim();
4066
+ }
4067
+ if (element.id) {
4068
+ const label = document.querySelector(`label[for="${element.id}"]`);
4069
+ if (label) return label.textContent?.trim();
4070
+ }
4071
+ const title = element.getAttribute("title");
4072
+ if (title) return title;
4073
+ const text = element.textContent?.trim();
4074
+ if (text && text.length <= 50) return text;
4075
+ if (element instanceof HTMLInputElement || element instanceof HTMLTextAreaElement) {
4076
+ const placeholder = element.placeholder;
4077
+ if (placeholder) return placeholder;
4078
+ }
4079
+ return void 0;
4080
+ }
4081
+ function isElementVisible2(element) {
4082
+ const style = window.getComputedStyle(element);
4083
+ if (style.display === "none" || style.visibility === "hidden") {
4084
+ return false;
4085
+ }
4086
+ const rect = element.getBoundingClientRect();
4087
+ return rect.width > 0 && rect.height > 0;
4088
+ }
4089
+ function generateSemanticId(element) {
4090
+ const type = inferElementType2(element);
4091
+ const label = getAccessibleLabel(element);
4092
+ if (label) {
4093
+ const sanitized = label.toLowerCase().replace(/[^a-z0-9\s-]/g, "").replace(/\s+/g, "-").slice(0, 30);
4094
+ return `${type}-${sanitized}`;
4095
+ }
4096
+ const parent = element.parentElement;
4097
+ if (parent) {
4098
+ const siblings = Array.from(parent.querySelectorAll(element.tagName));
4099
+ const index = siblings.indexOf(element);
4100
+ return `${element.tagName.toLowerCase()}-${index}`;
4101
+ }
4102
+ return `${type}-${Date.now()}-${Math.random().toString(36).slice(2, 6)}`;
4103
+ }
4104
+ function generateAutoId(element) {
4105
+ const type = inferElementType2(element);
4106
+ return `${type}-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
4107
+ }
4108
+ function generateIdForElement(element, strategy, customGenerator) {
4109
+ if (customGenerator) {
4110
+ return customGenerator(element);
4111
+ }
4112
+ switch (strategy) {
4113
+ case "data-testid": {
4114
+ const testId = element.getAttribute("data-testid");
4115
+ return testId || generateAutoId(element);
4116
+ }
4117
+ case "data-ui-id": {
4118
+ const uiId = element.getAttribute("data-ui-id");
4119
+ return uiId || generateAutoId(element);
4120
+ }
4121
+ case "semantic":
4122
+ return generateSemanticId(element);
4123
+ case "auto":
4124
+ return generateAutoId(element);
4125
+ case "prefer-existing":
4126
+ default: {
4127
+ const uiId = element.getAttribute("data-ui-id");
4128
+ if (uiId) return uiId;
4129
+ const testId = element.getAttribute("data-testid");
4130
+ if (testId) return testId;
4131
+ const htmlId = element.id;
4132
+ if (htmlId) return htmlId;
4133
+ return generateSemanticId(element);
4134
+ }
4135
+ }
4136
+ }
4137
+ function useAutoRegister(options = {}) {
4138
+ const {
4139
+ enabled = process.env.NODE_ENV === "development",
4140
+ root = null,
4141
+ idStrategy = "prefer-existing",
4142
+ debounceMs = 100,
4143
+ includeHidden = false,
4144
+ includeSelectors = [],
4145
+ excludeSelectors = [],
4146
+ generateId: customGenerateId,
4147
+ onRegister,
4148
+ onUnregister
4149
+ } = options;
4150
+ const bridge = useUIBridgeOptional();
4151
+ const registeredElementsRef = react.useRef(/* @__PURE__ */ new Map());
4152
+ const pendingRegistrationsRef = react.useRef(/* @__PURE__ */ new Set());
4153
+ const debounceTimeoutRef = react.useRef(null);
4154
+ const shouldRegister = react.useCallback(
4155
+ (element) => {
4156
+ if (!includeHidden && !isElementVisible2(element)) {
4157
+ return false;
4158
+ }
4159
+ for (const selector of excludeSelectors) {
4160
+ if (element.matches(selector)) {
4161
+ return false;
4162
+ }
4163
+ }
4164
+ if (registeredElementsRef.current.has(element)) {
4165
+ return false;
4166
+ }
4167
+ const allSelectors = [...INTERACTIVE_SELECTORS2, ...includeSelectors];
4168
+ for (const selector of allSelectors) {
4169
+ if (element.matches(selector)) {
4170
+ return true;
4171
+ }
4172
+ }
4173
+ return false;
4174
+ },
4175
+ [includeHidden, includeSelectors, excludeSelectors]
4176
+ );
4177
+ const registerElement = react.useCallback(
4178
+ (element) => {
4179
+ if (!bridge?.registry || registeredElementsRef.current.has(element)) {
4180
+ return;
4181
+ }
4182
+ const id = generateIdForElement(element, idStrategy, customGenerateId);
4183
+ const existing = bridge.registry.getElement(id);
4184
+ if (existing) {
4185
+ const uniqueId = `${id}-${Date.now().toString(36)}`;
4186
+ const type = inferElementType2(element);
4187
+ const actions = inferActions2(type);
4188
+ const label = getAccessibleLabel(element);
4189
+ bridge.registry.registerElement(uniqueId, element, {
4190
+ type,
4191
+ actions,
4192
+ label
4193
+ });
4194
+ registeredElementsRef.current.set(element, uniqueId);
4195
+ onRegister?.(uniqueId, element);
4196
+ } else {
4197
+ const type = inferElementType2(element);
4198
+ const actions = inferActions2(type);
4199
+ const label = getAccessibleLabel(element);
4200
+ bridge.registry.registerElement(id, element, {
4201
+ type,
4202
+ actions,
4203
+ label
4204
+ });
4205
+ registeredElementsRef.current.set(element, id);
4206
+ onRegister?.(id, element);
4207
+ }
4208
+ },
4209
+ [bridge, idStrategy, customGenerateId, onRegister]
4210
+ );
4211
+ const unregisterElement = react.useCallback(
4212
+ (element) => {
4213
+ const id = registeredElementsRef.current.get(element);
4214
+ if (!id || !bridge?.registry) return;
4215
+ bridge.registry.unregisterElement(id);
4216
+ registeredElementsRef.current.delete(element);
4217
+ onUnregister?.(id);
4218
+ },
4219
+ [bridge, onUnregister]
4220
+ );
4221
+ const processPendingRegistrations = react.useCallback(() => {
4222
+ pendingRegistrationsRef.current.forEach((element) => {
4223
+ if (shouldRegister(element)) {
4224
+ registerElement(element);
4225
+ }
4226
+ });
4227
+ pendingRegistrationsRef.current.clear();
4228
+ }, [shouldRegister, registerElement]);
4229
+ const queueRegistration = react.useCallback(
4230
+ (element) => {
4231
+ pendingRegistrationsRef.current.add(element);
4232
+ if (debounceTimeoutRef.current) {
4233
+ clearTimeout(debounceTimeoutRef.current);
4234
+ }
4235
+ debounceTimeoutRef.current = setTimeout(processPendingRegistrations, debounceMs);
4236
+ },
4237
+ [debounceMs, processPendingRegistrations]
4238
+ );
4239
+ const scanAndRegister = react.useCallback(
4240
+ (rootElement) => {
4241
+ const allSelectors = [...INTERACTIVE_SELECTORS2, ...includeSelectors].join(", ");
4242
+ const elements = rootElement.querySelectorAll(allSelectors);
4243
+ elements.forEach((element) => {
4244
+ if (shouldRegister(element)) {
4245
+ queueRegistration(element);
4246
+ }
4247
+ });
4248
+ },
4249
+ [includeSelectors, shouldRegister, queueRegistration]
4250
+ );
4251
+ const handleMutations = react.useCallback(
4252
+ (mutations) => {
4253
+ mutations.forEach((mutation) => {
4254
+ mutation.addedNodes.forEach((node) => {
4255
+ if (node.nodeType === Node.ELEMENT_NODE) {
4256
+ const element = node;
4257
+ if (shouldRegister(element)) {
4258
+ queueRegistration(element);
4259
+ }
4260
+ const allSelectors = [...INTERACTIVE_SELECTORS2, ...includeSelectors].join(", ");
4261
+ const descendants = element.querySelectorAll(allSelectors);
4262
+ descendants.forEach((descendant) => {
4263
+ if (shouldRegister(descendant)) {
4264
+ queueRegistration(descendant);
4265
+ }
4266
+ });
4267
+ }
4268
+ });
4269
+ mutation.removedNodes.forEach((node) => {
4270
+ if (node.nodeType === Node.ELEMENT_NODE) {
4271
+ const element = node;
4272
+ if (registeredElementsRef.current.has(element)) {
4273
+ unregisterElement(element);
4274
+ }
4275
+ const descendants = element.querySelectorAll("*");
4276
+ descendants.forEach((descendant) => {
4277
+ if (registeredElementsRef.current.has(descendant)) {
4278
+ unregisterElement(descendant);
4279
+ }
4280
+ });
4281
+ }
4282
+ });
4283
+ });
4284
+ },
4285
+ [shouldRegister, queueRegistration, unregisterElement, includeSelectors]
4286
+ );
4287
+ react.useEffect(() => {
4288
+ if (!enabled || !bridge?.registry) return;
4289
+ const rootElement = root || document.body;
4290
+ scanAndRegister(rootElement);
4291
+ const observer = new MutationObserver(handleMutations);
4292
+ observer.observe(rootElement, {
4293
+ childList: true,
4294
+ subtree: true
4295
+ });
4296
+ return () => {
4297
+ observer.disconnect();
4298
+ if (debounceTimeoutRef.current) {
4299
+ clearTimeout(debounceTimeoutRef.current);
4300
+ }
4301
+ registeredElementsRef.current.forEach((id, _element) => {
4302
+ bridge.registry.unregisterElement(id);
4303
+ });
4304
+ registeredElementsRef.current.clear();
4305
+ };
4306
+ }, [enabled, bridge, root, scanAndRegister, handleMutations]);
4307
+ }
4308
+ function AutoRegisterProvider({
4309
+ children,
4310
+ scopeToChildren = false,
4311
+ enabled = process.env.NODE_ENV === "development",
4312
+ idStrategy = "prefer-existing",
4313
+ debounceMs = 100,
4314
+ includeHidden = false,
4315
+ includeSelectors = [],
4316
+ excludeSelectors = [],
4317
+ generateId: generateId4,
4318
+ onRegister,
4319
+ onUnregister
4320
+ }) {
4321
+ const containerRef = react.useRef(null);
4322
+ useAutoRegister({
4323
+ enabled,
4324
+ root: scopeToChildren ? containerRef.current : null,
4325
+ idStrategy,
4326
+ debounceMs,
4327
+ includeHidden,
4328
+ includeSelectors,
4329
+ excludeSelectors,
4330
+ generateId: generateId4,
4331
+ onRegister,
4332
+ onUnregister
4333
+ });
4334
+ if (scopeToChildren) {
4335
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { ref: containerRef, style: { display: "contents" }, children });
4336
+ }
4337
+ return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children });
4338
+ }
4339
+ var overlayStyles = {
4340
+ position: "fixed",
4341
+ pointerEvents: "none",
4342
+ zIndex: 999999,
4343
+ border: "2px solid #3b82f6",
4344
+ backgroundColor: "rgba(59, 130, 246, 0.1)",
4345
+ transition: "all 0.1s ease-out"
4346
+ };
4347
+ var labelStyles = {
4348
+ position: "absolute",
4349
+ top: "-24px",
4350
+ left: "0",
4351
+ padding: "2px 8px",
4352
+ backgroundColor: "#3b82f6",
4353
+ color: "white",
4354
+ fontSize: "12px",
4355
+ fontFamily: "monospace",
4356
+ whiteSpace: "nowrap",
4357
+ borderRadius: "4px 4px 0 0"
4358
+ };
4359
+ var panelStyles = {
4360
+ position: "fixed",
4361
+ bottom: "20px",
4362
+ right: "20px",
4363
+ width: "400px",
4364
+ maxHeight: "500px",
4365
+ overflow: "auto",
4366
+ backgroundColor: "#1f2937",
4367
+ color: "#f3f4f6",
4368
+ borderRadius: "8px",
4369
+ boxShadow: "0 4px 20px rgba(0, 0, 0, 0.3)",
4370
+ fontFamily: "monospace",
4371
+ fontSize: "12px",
4372
+ zIndex: 999998
4373
+ };
4374
+ var headerStyles = {
4375
+ padding: "12px 16px",
4376
+ backgroundColor: "#111827",
4377
+ borderBottom: "1px solid #374151",
4378
+ display: "flex",
4379
+ justifyContent: "space-between",
4380
+ alignItems: "center",
4381
+ borderRadius: "8px 8px 0 0"
4382
+ };
4383
+ var sectionStyles = {
4384
+ padding: "12px 16px",
4385
+ borderBottom: "1px solid #374151"
4386
+ };
4387
+ var labelKeyStyles = {
4388
+ color: "#9ca3af",
4389
+ marginRight: "8px"
4390
+ };
4391
+ var valueStyles = {
4392
+ color: "#60a5fa"
4393
+ };
4394
+ function getElementState4(element) {
4395
+ const rect = element.getBoundingClientRect();
4396
+ const style = window.getComputedStyle(element);
4397
+ const state = {
4398
+ visible: rect.width > 0 && rect.height > 0 && style.display !== "none",
4399
+ enabled: !("disabled" in element && element.disabled),
4400
+ focused: document.activeElement === element,
4401
+ rect: {
4402
+ x: rect.x,
4403
+ y: rect.y,
4404
+ width: rect.width,
4405
+ height: rect.height,
4406
+ top: rect.top,
4407
+ right: rect.right,
4408
+ bottom: rect.bottom,
4409
+ left: rect.left
4410
+ },
4411
+ computedStyles: {
4412
+ display: style.display,
4413
+ visibility: style.visibility,
4414
+ opacity: style.opacity,
4415
+ pointerEvents: style.pointerEvents
4416
+ }
4417
+ };
4418
+ if (element instanceof HTMLInputElement) {
4419
+ state.value = element.value;
4420
+ if (element.type === "checkbox" || element.type === "radio") {
4421
+ state.checked = element.checked;
4422
+ }
4423
+ }
4424
+ return state;
4425
+ }
4426
+ function InspectorOverlay({ bounds, label }) {
4427
+ return /* @__PURE__ */ jsxRuntime.jsx(
4428
+ "div",
4429
+ {
4430
+ style: {
4431
+ ...overlayStyles,
4432
+ left: bounds.left + window.scrollX,
4433
+ top: bounds.top + window.scrollY,
4434
+ width: bounds.width,
4435
+ height: bounds.height
4436
+ },
4437
+ children: /* @__PURE__ */ jsxRuntime.jsx("span", { style: labelStyles, children: label })
4438
+ }
4439
+ );
4440
+ }
4441
+ function InfoPanel({ element, onClose, registeredElement }) {
4442
+ if (!element) return null;
4443
+ const identifier = createElementIdentifier(element);
4444
+ const state = getElementState4(element);
4445
+ const bestId = getBestIdentifier(element);
4446
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: panelStyles, children: [
4447
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: headerStyles, children: [
4448
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: { fontWeight: "bold", color: "#60a5fa" }, children: "UI Bridge Inspector" }),
4449
+ /* @__PURE__ */ jsxRuntime.jsx(
4450
+ "button",
4451
+ {
4452
+ onClick: onClose,
4453
+ style: {
4454
+ background: "none",
4455
+ border: "none",
4456
+ color: "#9ca3af",
4457
+ cursor: "pointer",
4458
+ fontSize: "16px"
4459
+ },
4460
+ children: "\xD7"
4461
+ }
4462
+ )
4463
+ ] }),
4464
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: sectionStyles, children: [
4465
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: { marginBottom: "8px", fontWeight: "bold", color: "#f3f4f6" }, children: "Element" }),
4466
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
4467
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: labelKeyStyles, children: "Tag:" }),
4468
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: valueStyles, children: element.tagName.toLowerCase() })
4469
+ ] }),
4470
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
4471
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: labelKeyStyles, children: "Best ID:" }),
4472
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: valueStyles, children: bestId })
4473
+ ] }),
4474
+ identifier.uiId && /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
4475
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: labelKeyStyles, children: "data-ui-id:" }),
4476
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: valueStyles, children: identifier.uiId })
4477
+ ] }),
4478
+ identifier.testId && /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
4479
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: labelKeyStyles, children: "data-testid:" }),
4480
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: valueStyles, children: identifier.testId })
4481
+ ] }),
4482
+ identifier.htmlId && /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
4483
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: labelKeyStyles, children: "id:" }),
4484
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: valueStyles, children: identifier.htmlId })
4485
+ ] }),
4486
+ registeredElement && /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
4487
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: labelKeyStyles, children: "Registered:" }),
4488
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { style: { color: "#10b981" }, children: [
4489
+ "Yes (",
4490
+ registeredElement.type,
4491
+ ")"
4492
+ ] })
4493
+ ] })
4494
+ ] }),
4495
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: sectionStyles, children: [
4496
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: { marginBottom: "8px", fontWeight: "bold", color: "#f3f4f6" }, children: "State" }),
4497
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
4498
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: labelKeyStyles, children: "Visible:" }),
4499
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: { color: state.visible ? "#10b981" : "#ef4444" }, children: state.visible ? "Yes" : "No" })
4500
+ ] }),
4501
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
4502
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: labelKeyStyles, children: "Enabled:" }),
4503
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: { color: state.enabled ? "#10b981" : "#ef4444" }, children: state.enabled ? "Yes" : "No" })
4504
+ ] }),
4505
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
4506
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: labelKeyStyles, children: "Focused:" }),
4507
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: { color: state.focused ? "#10b981" : "#9ca3af" }, children: state.focused ? "Yes" : "No" })
4508
+ ] }),
4509
+ state.value !== void 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
4510
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: labelKeyStyles, children: "Value:" }),
4511
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { style: valueStyles, children: [
4512
+ '"',
4513
+ state.value,
4514
+ '"'
4515
+ ] })
4516
+ ] }),
4517
+ state.checked !== void 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
4518
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: labelKeyStyles, children: "Checked:" }),
4519
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: { color: state.checked ? "#10b981" : "#9ca3af" }, children: state.checked ? "Yes" : "No" })
4520
+ ] })
4521
+ ] }),
4522
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: sectionStyles, children: [
4523
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: { marginBottom: "8px", fontWeight: "bold", color: "#f3f4f6" }, children: "Bounds" }),
4524
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
4525
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: labelKeyStyles, children: "Position:" }),
4526
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { style: valueStyles, children: [
4527
+ "(",
4528
+ Math.round(state.rect.x),
4529
+ ", ",
4530
+ Math.round(state.rect.y),
4531
+ ")"
4532
+ ] })
4533
+ ] }),
4534
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
4535
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: labelKeyStyles, children: "Size:" }),
4536
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { style: valueStyles, children: [
4537
+ Math.round(state.rect.width),
4538
+ " \xD7 ",
4539
+ Math.round(state.rect.height)
4540
+ ] })
4541
+ ] })
4542
+ ] }),
4543
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { ...sectionStyles, borderBottom: "none" }, children: [
4544
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: { marginBottom: "8px", fontWeight: "bold", color: "#f3f4f6" }, children: "Selectors" }),
4545
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { wordBreak: "break-all", marginBottom: "4px" }, children: [
4546
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: labelKeyStyles, children: "CSS:" }),
4547
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: valueStyles, children: identifier.selector })
4548
+ ] }),
4549
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { wordBreak: "break-all" }, children: [
4550
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: labelKeyStyles, children: "XPath:" }),
4551
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: valueStyles, children: identifier.xpath })
4552
+ ] })
4553
+ ] }),
4554
+ registeredElement && registeredElement.actions.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { ...sectionStyles, borderBottom: "none" }, children: [
4555
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: { marginBottom: "8px", fontWeight: "bold", color: "#f3f4f6" }, children: "Actions" }),
4556
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: { display: "flex", flexWrap: "wrap", gap: "4px" }, children: registeredElement.actions.map((action) => /* @__PURE__ */ jsxRuntime.jsx(
4557
+ "span",
4558
+ {
4559
+ style: {
4560
+ padding: "2px 8px",
4561
+ backgroundColor: "#374151",
4562
+ borderRadius: "4px",
4563
+ color: "#60a5fa"
4564
+ },
4565
+ children: action
4566
+ },
4567
+ action
4568
+ )) })
4569
+ ] })
4570
+ ] });
4571
+ }
4572
+ function useInspector(options = {}) {
4573
+ const [active, setActive] = react.useState(false);
4574
+ const [hoveredElement, setHoveredElement] = react.useState(null);
4575
+ const [selectedElement, setSelectedElement] = react.useState(null);
4576
+ const shortcut = react.useMemo(
4577
+ () => options.shortcut ?? { key: "i", ctrl: true, shift: true },
4578
+ [options.shortcut]
4579
+ );
4580
+ const toggle = react.useCallback(() => {
4581
+ setActive((prev) => !prev);
4582
+ if (active) {
4583
+ setHoveredElement(null);
4584
+ setSelectedElement(null);
4585
+ }
4586
+ }, [active]);
4587
+ react.useEffect(() => {
4588
+ const handleKeyDown = (e) => {
4589
+ if (e.key.toLowerCase() === shortcut.key && e.ctrlKey === !!shortcut.ctrl && e.shiftKey === !!shortcut.shift && e.altKey === !!shortcut.alt) {
4590
+ e.preventDefault();
4591
+ toggle();
4592
+ }
4593
+ };
4594
+ window.addEventListener("keydown", handleKeyDown);
4595
+ return () => window.removeEventListener("keydown", handleKeyDown);
4596
+ }, [toggle, shortcut]);
4597
+ react.useEffect(() => {
4598
+ if (!active) return;
4599
+ const handleMouseOver = (e) => {
4600
+ const target = e.target;
4601
+ if (target && target !== hoveredElement) {
4602
+ setHoveredElement(target);
4603
+ }
4604
+ };
4605
+ const handleClick = (e) => {
4606
+ e.preventDefault();
4607
+ e.stopPropagation();
4608
+ const target = e.target;
4609
+ setSelectedElement(target);
4610
+ options.onSelect?.(target);
4611
+ };
4612
+ document.addEventListener("mouseover", handleMouseOver, true);
4613
+ document.addEventListener("click", handleClick, true);
4614
+ return () => {
4615
+ document.removeEventListener("mouseover", handleMouseOver, true);
4616
+ document.removeEventListener("click", handleClick, true);
4617
+ };
4618
+ }, [active, hoveredElement, options]);
4619
+ const displayElement = selectedElement || hoveredElement;
4620
+ const bounds = displayElement?.getBoundingClientRect() || null;
4621
+ const registeredElement = displayElement ? options.getRegisteredElement?.(displayElement) : void 0;
4622
+ return {
4623
+ active,
4624
+ toggle,
4625
+ hoveredElement,
4626
+ selectedElement,
4627
+ setSelectedElement,
4628
+ displayElement,
4629
+ bounds,
4630
+ registeredElement,
4631
+ clearSelection: () => {
4632
+ setSelectedElement(null);
4633
+ }
4634
+ };
4635
+ }
4636
+ function Inspector({ getRegisteredElement, initialActive }) {
4637
+ const inspector = useInspector({
4638
+ getRegisteredElement
4639
+ });
4640
+ react.useEffect(() => {
4641
+ if (initialActive && !inspector.active) {
4642
+ inspector.toggle();
4643
+ }
4644
+ }, [initialActive]);
4645
+ if (!inspector.active) return null;
4646
+ return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
4647
+ inspector.bounds && /* @__PURE__ */ jsxRuntime.jsx(
4648
+ InspectorOverlay,
4649
+ {
4650
+ bounds: inspector.bounds,
4651
+ label: inspector.displayElement ? getBestIdentifier(inspector.displayElement) : ""
4652
+ }
4653
+ ),
4654
+ inspector.selectedElement && /* @__PURE__ */ jsxRuntime.jsx(
4655
+ InfoPanel,
4656
+ {
4657
+ element: inspector.selectedElement,
4658
+ onClose: inspector.clearSelection,
4659
+ registeredElement: inspector.registeredElement
4660
+ }
4661
+ )
4662
+ ] });
4663
+ }
4664
+
4665
+ exports.AutoRegisterProvider = AutoRegisterProvider;
4666
+ exports.DOMChangeObserver = DOMChangeObserver;
4667
+ exports.DefaultActionExecutor = DefaultActionExecutor;
4668
+ exports.DefaultWorkflowEngine = DefaultWorkflowEngine;
4669
+ exports.ID_ATTRIBUTES = ID_ATTRIBUTES;
4670
+ exports.InMemoryRenderLogStorage = InMemoryRenderLogStorage;
4671
+ exports.InfoPanel = InfoPanel;
4672
+ exports.Inspector = Inspector;
4673
+ exports.InspectorOverlay = InspectorOverlay;
4674
+ exports.MetricsCollector = MetricsCollector;
4675
+ exports.RenderLogManager = RenderLogManager;
4676
+ exports.UIBridgeProvider = UIBridgeProvider;
4677
+ exports.UIBridgeRegistry = UIBridgeRegistry;
4678
+ exports.UIBridgeWSClient = UIBridgeWSClient;
4679
+ exports.captureDOMSnapshot = captureDOMSnapshot;
4680
+ exports.captureInteractiveElements = captureInteractiveElements;
4681
+ exports.createActionExecutor = createActionExecutor;
4682
+ exports.createElementIdentifier = createElementIdentifier;
4683
+ exports.createMetricsCollector = createMetricsCollector;
4684
+ exports.createRenderLogManager = createRenderLogManager;
4685
+ exports.createWSClient = createWSClient;
4686
+ exports.createWorkflowEngine = createWorkflowEngine;
4687
+ exports.elementMatchesIdentifier = elementMatchesIdentifier;
4688
+ exports.findAllElementsByIdentifier = findAllElementsByIdentifier;
4689
+ exports.findElementByIdentifier = findElementByIdentifier;
4690
+ exports.formatDuration = formatDuration;
4691
+ exports.formatPercentage = formatPercentage;
4692
+ exports.generateCSSSelector = generateCSSSelector;
4693
+ exports.generateXPath = generateXPath;
4694
+ exports.getBestIdentifier = getBestIdentifier;
4695
+ exports.getGlobalRegistry = getGlobalRegistry;
4696
+ exports.resetGlobalRegistry = resetGlobalRegistry;
4697
+ exports.setGlobalRegistry = setGlobalRegistry;
4698
+ exports.useActiveStates = useActiveStates;
4699
+ exports.useAutoRegister = useAutoRegister;
4700
+ exports.useAvailableTransitions = useAvailableTransitions;
4701
+ exports.useCanNavigateTo = useCanNavigateTo;
4702
+ exports.useInspector = useInspector;
4703
+ exports.useNavigationPath = useNavigationPath;
4704
+ exports.useStateSnapshot = useStateSnapshot;
4705
+ exports.useTransitions = useTransitions;
4706
+ exports.useUIBridge = useUIBridge;
4707
+ exports.useUIBridgeContext = useUIBridgeContext;
4708
+ exports.useUIBridgeOptional = useUIBridgeOptional;
4709
+ exports.useUIBridgeRequired = useUIBridgeRequired;
4710
+ exports.useUIComponent = useUIComponent;
4711
+ exports.useUIComponentAction = useUIComponentAction;
4712
+ exports.useUIElement = useUIElement;
4713
+ exports.useUIElementRef = useUIElementRef;
4714
+ exports.useUINavigation = useUINavigation;
4715
+ exports.useUIState = useUIState;
4716
+ exports.useUIStateGroup = useUIStateGroup;
4717
+ exports.useUITransition = useUITransition;
4718
+ //# sourceMappingURL=index.js.map
4719
+ //# sourceMappingURL=index.js.map