shellx-ai 1.0.11 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +586 -0
- package/dist/automation/element-finder.d.ts +189 -0
- package/dist/automation/element-finder.js +322 -0
- package/dist/automation/element-finder.js.map +1 -0
- package/dist/automation/ui-action-handler.d.ts +330 -0
- package/dist/automation/ui-action-handler.js +873 -0
- package/dist/automation/ui-action-handler.js.map +1 -0
- package/dist/cbor-compat.d.ts +27 -0
- package/dist/cbor-compat.js +108 -0
- package/dist/cbor-compat.js.map +1 -0
- package/dist/domain-manager.d.ts +80 -0
- package/dist/domain-manager.js +158 -0
- package/dist/domain-manager.js.map +1 -0
- package/dist/error-handler.d.ts +87 -0
- package/dist/error-handler.js +148 -0
- package/dist/error-handler.js.map +1 -0
- package/dist/errors.d.ts +114 -0
- package/dist/errors.js +139 -0
- package/dist/errors.js.map +1 -0
- package/dist/index.d.ts +163 -54
- package/dist/index.js +712 -472
- package/dist/index.js.map +1 -0
- package/dist/logger.d.ts +81 -0
- package/dist/logger.js +128 -0
- package/dist/logger.js.map +1 -0
- package/dist/protocol.d.ts +147 -31
- package/dist/protocol.js +2 -2
- package/dist/protocol.js.map +1 -0
- package/dist/shell/output-buffer.d.ts +152 -0
- package/dist/shell/output-buffer.js +163 -0
- package/dist/shell/output-buffer.js.map +1 -0
- package/dist/shell/shell-command-executor.d.ts +182 -0
- package/dist/shell/shell-command-executor.js +348 -0
- package/dist/shell/shell-command-executor.js.map +1 -0
- package/dist/shellx.d.ts +681 -176
- package/dist/shellx.js +763 -1047
- package/dist/shellx.js.map +1 -0
- package/dist/types.d.ts +132 -57
- package/dist/types.js +4 -4
- package/dist/types.js.map +1 -0
- package/dist/utils/retry-helper.d.ts +73 -0
- package/dist/utils/retry-helper.js +92 -0
- package/dist/utils/retry-helper.js.map +1 -0
- package/dist/utils.d.ts +3 -3
- package/dist/utils.js +17 -23
- package/dist/utils.js.map +1 -0
- package/package.json +95 -59
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ElementFinder - A module for finding and managing UI elements
|
|
3
|
+
*
|
|
4
|
+
* This module provides functionality to search for UI elements with various
|
|
5
|
+
* selectors, retry logic, and helper methods for element navigation.
|
|
6
|
+
*/
|
|
7
|
+
import type { ElementSelector, UIElement, ScreenInfoResponse, FindOptions } from "../protocol.js";
|
|
8
|
+
import type { Element } from "../types.js";
|
|
9
|
+
/**
|
|
10
|
+
* Interface for ConnectionClient methods used by ElementFinder
|
|
11
|
+
*/
|
|
12
|
+
interface IConnectionClient {
|
|
13
|
+
findElement(selector: ElementSelector, options?: FindOptions): Promise<{
|
|
14
|
+
elements?: UIElement[];
|
|
15
|
+
}>;
|
|
16
|
+
getScreenInfo(): Promise<ScreenInfoResponse>;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Options for finding elements
|
|
20
|
+
*/
|
|
21
|
+
export interface FindElementOptions {
|
|
22
|
+
/** Maximum number of retry attempts */
|
|
23
|
+
maxRetries?: number;
|
|
24
|
+
/** Delay between retry attempts in milliseconds */
|
|
25
|
+
retryDelay?: number;
|
|
26
|
+
/** Maximum number of results to return */
|
|
27
|
+
maxResults?: number;
|
|
28
|
+
/** Whether to only return visible elements */
|
|
29
|
+
visibleOnly?: boolean;
|
|
30
|
+
/** Whether to only return clickable elements */
|
|
31
|
+
clickableOnly?: boolean;
|
|
32
|
+
/** Whether to find multiple elements */
|
|
33
|
+
multiple?: boolean;
|
|
34
|
+
/** Timeout for finding elements in milliseconds */
|
|
35
|
+
timeout?: number;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Options for scrolling to find elements
|
|
39
|
+
*/
|
|
40
|
+
export interface ScrollToFindOptions {
|
|
41
|
+
/** Maximum number of scroll attempts */
|
|
42
|
+
maxScrolls?: number;
|
|
43
|
+
/** Scroll direction */
|
|
44
|
+
direction?: "up" | "down" | "left" | "right";
|
|
45
|
+
/** Scroll distance in pixels */
|
|
46
|
+
distance?: number;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* ElementFinder class handles UI element discovery and navigation
|
|
50
|
+
*
|
|
51
|
+
* This class provides methods to:
|
|
52
|
+
* - Find single or multiple elements with retry logic
|
|
53
|
+
* - Navigate through app UI using text paths
|
|
54
|
+
* - Scroll to find elements
|
|
55
|
+
* - Wait for elements to appear or disappear
|
|
56
|
+
*/
|
|
57
|
+
export declare class ElementFinder {
|
|
58
|
+
private client;
|
|
59
|
+
private logger;
|
|
60
|
+
/**
|
|
61
|
+
* Creates an ElementFinder instance
|
|
62
|
+
*
|
|
63
|
+
* @param client - The ConnectionClient instance for element operations
|
|
64
|
+
*/
|
|
65
|
+
constructor(client: IConnectionClient);
|
|
66
|
+
/**
|
|
67
|
+
* Convert UIElement to simplified Element type
|
|
68
|
+
*
|
|
69
|
+
* @param uiElement - The UIElement to convert
|
|
70
|
+
* @returns Converted Element object
|
|
71
|
+
*/
|
|
72
|
+
private convertElement;
|
|
73
|
+
/**
|
|
74
|
+
* Find a single element with retry logic
|
|
75
|
+
*
|
|
76
|
+
* @param selector - The element selector criteria
|
|
77
|
+
* @param options - Find options with retry configuration
|
|
78
|
+
* @returns Promise resolving to the found UIElement or null if not found
|
|
79
|
+
*
|
|
80
|
+
* @example
|
|
81
|
+
* ```typescript
|
|
82
|
+
* const element = await elementFinder.findElement(
|
|
83
|
+
* { text: 'Submit', visible: true },
|
|
84
|
+
* { maxRetries: 3, retryDelay: 1000 }
|
|
85
|
+
* );
|
|
86
|
+
* ```
|
|
87
|
+
*/
|
|
88
|
+
findElement(selector: ElementSelector, options?: FindElementOptions): Promise<UIElement | null>;
|
|
89
|
+
/**
|
|
90
|
+
* Find multiple elements with retry logic
|
|
91
|
+
*
|
|
92
|
+
* @param selector - The element selector criteria
|
|
93
|
+
* @param options - Find options with retry configuration
|
|
94
|
+
* @returns Promise resolving to an array of UIElements
|
|
95
|
+
*
|
|
96
|
+
* @example
|
|
97
|
+
* ```typescript
|
|
98
|
+
* const elements = await elementFinder.findElements(
|
|
99
|
+
* { className: 'Button', visible: true },
|
|
100
|
+
* { maxResults: 10, visibleOnly: true }
|
|
101
|
+
* );
|
|
102
|
+
* ```
|
|
103
|
+
*/
|
|
104
|
+
findElements(selector: ElementSelector, options?: FindElementOptions): Promise<UIElement[]>;
|
|
105
|
+
/**
|
|
106
|
+
* Find elements and return simplified Element objects
|
|
107
|
+
*
|
|
108
|
+
* @param selector - The element selector criteria
|
|
109
|
+
* @param options - Find options
|
|
110
|
+
* @returns Promise resolving to an array of simplified Element objects
|
|
111
|
+
*/
|
|
112
|
+
findElementsAsSimple(selector: ElementSelector, options?: FindElementOptions): Promise<Element[]>;
|
|
113
|
+
/**
|
|
114
|
+
* Wait for any of multiple elements to appear
|
|
115
|
+
* @deprecated Use waitAnyElement() instead for consistency
|
|
116
|
+
*/
|
|
117
|
+
waitForAnyElement(selectors: ElementSelector[], timeout?: number): Promise<{
|
|
118
|
+
element: UIElement;
|
|
119
|
+
selectorIndex: number;
|
|
120
|
+
} | null>;
|
|
121
|
+
/**
|
|
122
|
+
* Wait for any of multiple elements to appear
|
|
123
|
+
*
|
|
124
|
+
* @param selectors - Array of element selectors to check
|
|
125
|
+
* @param timeout - Maximum wait time in milliseconds (default: 10000)
|
|
126
|
+
* @returns Promise resolving to the first found element and its selector index, or null
|
|
127
|
+
*
|
|
128
|
+
* @example
|
|
129
|
+
* ```typescript
|
|
130
|
+
* const result = await elementFinder.waitAnyElement(
|
|
131
|
+
* [
|
|
132
|
+
* { text: 'Submit' },
|
|
133
|
+
* { text: 'OK' },
|
|
134
|
+
* { text: 'Confirm' }
|
|
135
|
+
* ],
|
|
136
|
+
* 10000
|
|
137
|
+
* );
|
|
138
|
+
* if (result) {
|
|
139
|
+
* console.log(`Found element at selector index: ${result.selectorIndex}`);
|
|
140
|
+
* }
|
|
141
|
+
* ```
|
|
142
|
+
*/
|
|
143
|
+
waitAnyElement(selectors: ElementSelector[], timeout?: number): Promise<{
|
|
144
|
+
element: UIElement;
|
|
145
|
+
selectorIndex: number;
|
|
146
|
+
} | null>;
|
|
147
|
+
/**
|
|
148
|
+
* Scroll to find an element
|
|
149
|
+
*
|
|
150
|
+
* This method performs scroll actions in the specified direction
|
|
151
|
+
* to find an element that's not currently visible on screen.
|
|
152
|
+
*
|
|
153
|
+
* @param selector - The element selector criteria
|
|
154
|
+
* @param swipeCallback - Callback function to perform swipe action
|
|
155
|
+
* @param options - Scroll options
|
|
156
|
+
* @returns Promise resolving to the found UIElement or null
|
|
157
|
+
*
|
|
158
|
+
* @example
|
|
159
|
+
* ```typescript
|
|
160
|
+
* const element = await elementFinder.scrollToFindElement(
|
|
161
|
+
* { text: 'Target' },
|
|
162
|
+
* async (from, to) => await shellx.swipe({ fromX: from.x, fromY: from.y, toX: to.x, toY: to.y }),
|
|
163
|
+
* { maxScrolls: 5, direction: 'down' }
|
|
164
|
+
* );
|
|
165
|
+
* ```
|
|
166
|
+
*/
|
|
167
|
+
scrollToFindElement(selector: ElementSelector, swipeCallback: (from: {
|
|
168
|
+
x: number;
|
|
169
|
+
y: number;
|
|
170
|
+
}, to: {
|
|
171
|
+
x: number;
|
|
172
|
+
y: number;
|
|
173
|
+
}) => Promise<void>, options?: ScrollToFindOptions): Promise<UIElement | null>;
|
|
174
|
+
/**
|
|
175
|
+
* Print information about a single element
|
|
176
|
+
*
|
|
177
|
+
* @param element - The UIElement to print
|
|
178
|
+
* @param index - Optional index number for display
|
|
179
|
+
*/
|
|
180
|
+
printElementInfo(element: UIElement, index?: number): void;
|
|
181
|
+
/**
|
|
182
|
+
* Print summary information about multiple elements
|
|
183
|
+
*
|
|
184
|
+
* @param elements - Array of UIElements to summarize
|
|
185
|
+
* @param title - Optional title for the summary
|
|
186
|
+
*/
|
|
187
|
+
printElementsInfo(elements: UIElement[], title?: string): void;
|
|
188
|
+
}
|
|
189
|
+
export {};
|
|
@@ -0,0 +1,322 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ElementFinder - A module for finding and managing UI elements
|
|
3
|
+
*
|
|
4
|
+
* This module provides functionality to search for UI elements with various
|
|
5
|
+
* selectors, retry logic, and helper methods for element navigation.
|
|
6
|
+
*/
|
|
7
|
+
import { createLogger } from "../logger.js";
|
|
8
|
+
/**
|
|
9
|
+
* ElementFinder class handles UI element discovery and navigation
|
|
10
|
+
*
|
|
11
|
+
* This class provides methods to:
|
|
12
|
+
* - Find single or multiple elements with retry logic
|
|
13
|
+
* - Navigate through app UI using text paths
|
|
14
|
+
* - Scroll to find elements
|
|
15
|
+
* - Wait for elements to appear or disappear
|
|
16
|
+
*/
|
|
17
|
+
export class ElementFinder {
|
|
18
|
+
client;
|
|
19
|
+
logger = createLogger("ElementFinder");
|
|
20
|
+
/**
|
|
21
|
+
* Creates an ElementFinder instance
|
|
22
|
+
*
|
|
23
|
+
* @param client - The ConnectionClient instance for element operations
|
|
24
|
+
*/
|
|
25
|
+
constructor(client) {
|
|
26
|
+
this.client = client;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Convert UIElement to simplified Element type
|
|
30
|
+
*
|
|
31
|
+
* @param uiElement - The UIElement to convert
|
|
32
|
+
* @returns Converted Element object
|
|
33
|
+
*/
|
|
34
|
+
convertElement(uiElement) {
|
|
35
|
+
return {
|
|
36
|
+
id: uiElement.elementId,
|
|
37
|
+
text: uiElement.text,
|
|
38
|
+
class: uiElement.className,
|
|
39
|
+
left: uiElement.bounds.left,
|
|
40
|
+
top: uiElement.bounds.top,
|
|
41
|
+
right: uiElement.bounds.right,
|
|
42
|
+
bottom: uiElement.bounds.bottom,
|
|
43
|
+
visible: uiElement.visible,
|
|
44
|
+
clickable: uiElement.clickable,
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Find a single element with retry logic
|
|
49
|
+
*
|
|
50
|
+
* @param selector - The element selector criteria
|
|
51
|
+
* @param options - Find options with retry configuration
|
|
52
|
+
* @returns Promise resolving to the found UIElement or null if not found
|
|
53
|
+
*
|
|
54
|
+
* @example
|
|
55
|
+
* ```typescript
|
|
56
|
+
* const element = await elementFinder.findElement(
|
|
57
|
+
* { text: 'Submit', visible: true },
|
|
58
|
+
* { maxRetries: 3, retryDelay: 1000 }
|
|
59
|
+
* );
|
|
60
|
+
* ```
|
|
61
|
+
*/
|
|
62
|
+
async findElement(selector, options = {}) {
|
|
63
|
+
const { maxRetries = 3, retryDelay = 1000, maxResults = 1, visibleOnly = true, timeout = 3000, } = options;
|
|
64
|
+
for (let i = 0; i < maxRetries; i++) {
|
|
65
|
+
try {
|
|
66
|
+
const result = await this.client.findElement(selector, {
|
|
67
|
+
timeout,
|
|
68
|
+
visibleOnly,
|
|
69
|
+
maxResults,
|
|
70
|
+
});
|
|
71
|
+
if (!result || !Array.isArray(result.elements)) {
|
|
72
|
+
this.logger.warn("⚠️ Empty result or missing elements field");
|
|
73
|
+
continue;
|
|
74
|
+
}
|
|
75
|
+
if (result.elements.length > 0) {
|
|
76
|
+
return result.elements[0] ?? null;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
catch (error) {
|
|
80
|
+
this.logger.info(`Find attempt ${i + 1}/${maxRetries} failed:`, error);
|
|
81
|
+
}
|
|
82
|
+
if (i < maxRetries - 1) {
|
|
83
|
+
await new Promise((resolve) => setTimeout(resolve, retryDelay));
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
return null;
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Find multiple elements with retry logic
|
|
90
|
+
*
|
|
91
|
+
* @param selector - The element selector criteria
|
|
92
|
+
* @param options - Find options with retry configuration
|
|
93
|
+
* @returns Promise resolving to an array of UIElements
|
|
94
|
+
*
|
|
95
|
+
* @example
|
|
96
|
+
* ```typescript
|
|
97
|
+
* const elements = await elementFinder.findElements(
|
|
98
|
+
* { className: 'Button', visible: true },
|
|
99
|
+
* { maxResults: 10, visibleOnly: true }
|
|
100
|
+
* );
|
|
101
|
+
* ```
|
|
102
|
+
*/
|
|
103
|
+
async findElements(selector, options = {}) {
|
|
104
|
+
const { maxRetries = 3, retryDelay = 1000, maxResults = 10, visibleOnly = true, clickableOnly = false, timeout = 3000, } = options;
|
|
105
|
+
for (let i = 0; i < maxRetries; i++) {
|
|
106
|
+
try {
|
|
107
|
+
const result = await this.client.findElement(selector, {
|
|
108
|
+
timeout,
|
|
109
|
+
visibleOnly,
|
|
110
|
+
clickableOnly,
|
|
111
|
+
multiple: true,
|
|
112
|
+
maxResults,
|
|
113
|
+
});
|
|
114
|
+
if (!result || !Array.isArray(result.elements)) {
|
|
115
|
+
this.logger.warn("⚠️ Empty result or missing elements field");
|
|
116
|
+
continue;
|
|
117
|
+
}
|
|
118
|
+
if (result.elements.length > 0) {
|
|
119
|
+
this.logger.info(`🔍 Found ${result.elements.length} elements`);
|
|
120
|
+
return result.elements;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
catch (error) {
|
|
124
|
+
this.logger.info(`Find attempt ${i + 1}/${maxRetries} failed:`, error);
|
|
125
|
+
}
|
|
126
|
+
if (i < maxRetries - 1) {
|
|
127
|
+
await new Promise((resolve) => setTimeout(resolve, retryDelay));
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
this.logger.info("❌ No elements found");
|
|
131
|
+
return [];
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Find elements and return simplified Element objects
|
|
135
|
+
*
|
|
136
|
+
* @param selector - The element selector criteria
|
|
137
|
+
* @param options - Find options
|
|
138
|
+
* @returns Promise resolving to an array of simplified Element objects
|
|
139
|
+
*/
|
|
140
|
+
async findElementsAsSimple(selector, options = {}) {
|
|
141
|
+
const uiElements = await this.findElements(selector, options);
|
|
142
|
+
return uiElements.map((el) => this.convertElement(el));
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* Wait for any of multiple elements to appear
|
|
146
|
+
* @deprecated Use waitAnyElement() instead for consistency
|
|
147
|
+
*/
|
|
148
|
+
async waitForAnyElement(selectors, timeout = 10000) {
|
|
149
|
+
return this.waitAnyElement(selectors, timeout);
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* Wait for any of multiple elements to appear
|
|
153
|
+
*
|
|
154
|
+
* @param selectors - Array of element selectors to check
|
|
155
|
+
* @param timeout - Maximum wait time in milliseconds (default: 10000)
|
|
156
|
+
* @returns Promise resolving to the first found element and its selector index, or null
|
|
157
|
+
*
|
|
158
|
+
* @example
|
|
159
|
+
* ```typescript
|
|
160
|
+
* const result = await elementFinder.waitAnyElement(
|
|
161
|
+
* [
|
|
162
|
+
* { text: 'Submit' },
|
|
163
|
+
* { text: 'OK' },
|
|
164
|
+
* { text: 'Confirm' }
|
|
165
|
+
* ],
|
|
166
|
+
* 10000
|
|
167
|
+
* );
|
|
168
|
+
* if (result) {
|
|
169
|
+
* console.log(`Found element at selector index: ${result.selectorIndex}`);
|
|
170
|
+
* }
|
|
171
|
+
* ```
|
|
172
|
+
*/
|
|
173
|
+
async waitAnyElement(selectors, timeout = 10000) {
|
|
174
|
+
const startTime = Date.now();
|
|
175
|
+
const checkInterval = 500;
|
|
176
|
+
while (Date.now() - startTime < timeout) {
|
|
177
|
+
for (let i = 0; i < selectors.length; i++) {
|
|
178
|
+
try {
|
|
179
|
+
const selector = selectors[i];
|
|
180
|
+
if (!selector) {
|
|
181
|
+
continue;
|
|
182
|
+
}
|
|
183
|
+
const result = await this.client.findElement(selector, {
|
|
184
|
+
timeout: checkInterval,
|
|
185
|
+
maxResults: 1,
|
|
186
|
+
visibleOnly: true,
|
|
187
|
+
});
|
|
188
|
+
if (!result || !Array.isArray(result.elements)) {
|
|
189
|
+
continue;
|
|
190
|
+
}
|
|
191
|
+
if (result.elements.length > 0) {
|
|
192
|
+
const element = result.elements[0];
|
|
193
|
+
if (element) {
|
|
194
|
+
return { element, selectorIndex: i };
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
catch {
|
|
199
|
+
// Continue to next selector
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
await new Promise((resolve) => setTimeout(resolve, checkInterval));
|
|
203
|
+
}
|
|
204
|
+
return null;
|
|
205
|
+
}
|
|
206
|
+
/**
|
|
207
|
+
* Scroll to find an element
|
|
208
|
+
*
|
|
209
|
+
* This method performs scroll actions in the specified direction
|
|
210
|
+
* to find an element that's not currently visible on screen.
|
|
211
|
+
*
|
|
212
|
+
* @param selector - The element selector criteria
|
|
213
|
+
* @param swipeCallback - Callback function to perform swipe action
|
|
214
|
+
* @param options - Scroll options
|
|
215
|
+
* @returns Promise resolving to the found UIElement or null
|
|
216
|
+
*
|
|
217
|
+
* @example
|
|
218
|
+
* ```typescript
|
|
219
|
+
* const element = await elementFinder.scrollToFindElement(
|
|
220
|
+
* { text: 'Target' },
|
|
221
|
+
* async (from, to) => await shellx.swipe({ fromX: from.x, fromY: from.y, toX: to.x, toY: to.y }),
|
|
222
|
+
* { maxScrolls: 5, direction: 'down' }
|
|
223
|
+
* );
|
|
224
|
+
* ```
|
|
225
|
+
*/
|
|
226
|
+
async scrollToFindElement(selector, swipeCallback, options = {}) {
|
|
227
|
+
const { maxScrolls = 5, direction = "down", distance = 400 } = options;
|
|
228
|
+
// First try to find without scrolling
|
|
229
|
+
let element = await this.findElement(selector, { maxRetries: 1, retryDelay: 0 });
|
|
230
|
+
if (element)
|
|
231
|
+
return element;
|
|
232
|
+
// Get screen info for coordinates
|
|
233
|
+
const screenInfo = await this.client.getScreenInfo();
|
|
234
|
+
const centerX = (screenInfo.width || 1080) / 2;
|
|
235
|
+
const centerY = (screenInfo.height || 1920) / 2;
|
|
236
|
+
let from;
|
|
237
|
+
let to;
|
|
238
|
+
// Calculate scroll direction
|
|
239
|
+
switch (direction) {
|
|
240
|
+
case "up":
|
|
241
|
+
from = { x: centerX, y: centerY + distance / 2 };
|
|
242
|
+
to = { x: centerX, y: centerY - distance / 2 };
|
|
243
|
+
break;
|
|
244
|
+
case "down":
|
|
245
|
+
from = { x: centerX, y: centerY - distance / 2 };
|
|
246
|
+
to = { x: centerX, y: centerY + distance / 2 };
|
|
247
|
+
break;
|
|
248
|
+
case "left":
|
|
249
|
+
from = { x: centerX + distance / 2, y: centerY };
|
|
250
|
+
to = { x: centerX - distance / 2, y: centerY };
|
|
251
|
+
break;
|
|
252
|
+
case "right":
|
|
253
|
+
from = { x: centerX - distance / 2, y: centerY };
|
|
254
|
+
to = { x: centerX + distance / 2, y: centerY };
|
|
255
|
+
break;
|
|
256
|
+
}
|
|
257
|
+
// Try scrolling to find element
|
|
258
|
+
for (let i = 0; i < maxScrolls; i++) {
|
|
259
|
+
this.logger.info(`Scrolling to find element, attempt ${i + 1}/${maxScrolls}...`);
|
|
260
|
+
await swipeCallback(from, to);
|
|
261
|
+
element = await this.findElement(selector, { maxRetries: 1, retryDelay: 0 });
|
|
262
|
+
if (element) {
|
|
263
|
+
this.logger.info("✅ Found element after scrolling");
|
|
264
|
+
return element;
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
this.logger.info("❌ Element not found after scrolling");
|
|
268
|
+
return null;
|
|
269
|
+
}
|
|
270
|
+
/**
|
|
271
|
+
* Print information about a single element
|
|
272
|
+
*
|
|
273
|
+
* @param element - The UIElement to print
|
|
274
|
+
* @param index - Optional index number for display
|
|
275
|
+
*/
|
|
276
|
+
printElementInfo(element, index) {
|
|
277
|
+
const prefix = index !== undefined ? `📋 Element ${index + 1}:` : `📋 Element Info:`;
|
|
278
|
+
this.logger.info(`\n${prefix}`);
|
|
279
|
+
this.logger.info(` - Element ID: ${element.elementId}`);
|
|
280
|
+
this.logger.info(` - Class Name: ${element.className}`);
|
|
281
|
+
this.logger.info(` - Resource ID: ${element.resourceId}`);
|
|
282
|
+
this.logger.info(` - Text: "${element.text}"`);
|
|
283
|
+
this.logger.info(` - Description: "${element.describe}"`);
|
|
284
|
+
this.logger.info(` - Visible: ${element.visible}`);
|
|
285
|
+
this.logger.info(` - Clickable: ${element.clickable}`);
|
|
286
|
+
this.logger.info(` - Bounds: {left: ${element.bounds.left}, top: ${element.bounds.top}, right: ${element.bounds.right}, bottom: ${element.bounds.bottom}}`);
|
|
287
|
+
this.logger.info(` - Size: ${element.bounds.right - element.bounds.left} x ${element.bounds.bottom - element.bounds.top}`);
|
|
288
|
+
}
|
|
289
|
+
/**
|
|
290
|
+
* Print summary information about multiple elements
|
|
291
|
+
*
|
|
292
|
+
* @param elements - Array of UIElements to summarize
|
|
293
|
+
* @param title - Optional title for the summary
|
|
294
|
+
*/
|
|
295
|
+
printElementsInfo(elements, title) {
|
|
296
|
+
if (elements.length === 0) {
|
|
297
|
+
this.logger.info("❌ No elements to display");
|
|
298
|
+
return;
|
|
299
|
+
}
|
|
300
|
+
this.logger.info(`\n${title || `Found ${elements.length} elements`}:`);
|
|
301
|
+
// Statistics
|
|
302
|
+
const visibleCount = elements.filter((e) => e.visible).length;
|
|
303
|
+
const clickableCount = elements.filter((e) => e.clickable).length;
|
|
304
|
+
const withTextCount = elements.filter((e) => e.text && e.text.trim().length > 0).length;
|
|
305
|
+
this.logger.info(`\n📊 Statistics:`);
|
|
306
|
+
this.logger.info(` - Total elements: ${elements.length}`);
|
|
307
|
+
this.logger.info(` - Visible elements: ${visibleCount}`);
|
|
308
|
+
this.logger.info(` - Clickable elements: ${clickableCount}`);
|
|
309
|
+
this.logger.info(` - Elements with text: ${withTextCount}`);
|
|
310
|
+
// Unique text values
|
|
311
|
+
const uniqueTexts = [
|
|
312
|
+
...new Set(elements.map((e) => e.text).filter((text) => text && text.trim().length > 0)),
|
|
313
|
+
];
|
|
314
|
+
if (uniqueTexts.length > 0) {
|
|
315
|
+
this.logger.info(`\n📝 Unique text values:`);
|
|
316
|
+
uniqueTexts.forEach((text, index) => {
|
|
317
|
+
this.logger.info(` ${index + 1}. "${text}"`);
|
|
318
|
+
});
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
//# sourceMappingURL=element-finder.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"element-finder.js","sourceRoot":"","sources":["../../src/automation/element-finder.ts"],"names":[],"mappings":"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"}
|