react-native-ai-debugger 1.0.7 → 1.0.8

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/README.md CHANGED
@@ -17,7 +17,7 @@ An MCP (Model Context Protocol) server for AI-powered React Native debugging. En
17
17
  - **Android device control** - screenshots, tap, swipe, text input, key events via ADB
18
18
  - **iOS simulator control** - screenshots, app management, URL handling via simctl
19
19
  - **iOS UI automation** - tap, swipe, text input, button presses via IDB (optional)
20
- - **iOS accessibility inspection** - get UI element tree and element info at coordinates via IDB
20
+ - **Element-based UI automation** - find and wait for elements by text/label without screenshots (faster, cheaper)
21
21
 
22
22
  ## Requirements
23
23
 
@@ -133,55 +133,35 @@ Requires VS Code 1.102+ with Copilot ([docs](https://code.visualstudio.com/docs/
133
133
 
134
134
  ### Android (ADB)
135
135
 
136
- | Tool | Description |
137
- | ------------------------- | ---------------------------------------------------- |
138
- | `list_android_devices` | List connected Android devices and emulators via ADB |
139
- | `android_screenshot` | Take a screenshot from an Android device/emulator |
140
- | `android_install_app` | Install an APK on an Android device/emulator |
141
- | `android_launch_app` | Launch an app by package name |
142
- | `android_list_packages` | List installed packages (with optional filter) |
143
- | `android_tap` | Tap at specific coordinates on screen |
144
- | `android_long_press` | Long press at specific coordinates |
145
- | `android_swipe` | Swipe from one point to another |
146
- | `android_input_text` | Type text at current focus point |
147
- | `android_key_event` | Send key events (HOME, BACK, ENTER, etc.) |
148
- | `android_get_screen_size` | Get device screen resolution |
149
-
150
- ### Android Accessibility (UI Hierarchy)
151
-
152
- | Tool | Description |
153
- | ----------------------- | ----------------------------------------------------- |
154
- | `android_describe_all` | Get full UI hierarchy tree using uiautomator |
155
- | `android_describe_point`| Get UI element info at specific coordinates |
156
- | `android_tap_element` | Tap element by text, content-desc, or resource-id |
136
+ | Tool | Description |
137
+ | --------------------------- | ------------------------------------------------------------- |
138
+ | `list_android_devices` | List connected Android devices and emulators via ADB |
139
+ | `android_screenshot` | Take a screenshot from an Android device/emulator |
140
+ | `android_install_app` | Install an APK on an Android device/emulator |
141
+ | `android_launch_app` | Launch an app by package name |
142
+ | `android_list_packages` | List installed packages (with optional filter) |
143
+ | `android_tap` | Tap at specific coordinates on screen |
144
+ | `android_long_press` | Long press at specific coordinates |
145
+ | `android_swipe` | Swipe from one point to another |
146
+ | `android_input_text` | Type text at current focus point |
147
+ | `android_key_event` | Send key events (HOME, BACK, ENTER, etc.) |
148
+ | `android_get_screen_size` | Get device screen resolution |
149
+ | `android_find_element` | Find element by text/contentDesc/resourceId (no screenshot) |
150
+ | `android_wait_for_element` | Wait for element to appear (useful for screen transitions) |
157
151
 
158
152
  ### iOS (Simulator)
159
153
 
160
- | Tool | Description |
161
- | --------------------- | ------------------------------------------- |
162
- | `list_ios_simulators` | List available iOS simulators |
163
- | `ios_screenshot` | Take a screenshot from an iOS simulator |
164
- | `ios_install_app` | Install an app bundle (.app) on a simulator |
165
- | `ios_launch_app` | Launch an app by bundle ID |
166
- | `ios_open_url` | Open a URL (deep links or web URLs) |
167
- | `ios_terminate_app` | Terminate a running app |
168
- | `ios_boot_simulator` | Boot a simulator by UDID |
169
-
170
- ### iOS UI Interaction (requires IDB)
171
-
172
- These tools require [Facebook IDB](https://fbidb.io/) to be installed: `brew install idb-companion`
173
-
174
- | Tool | Description |
175
- | ------------------- | ----------------------------------------------------- |
176
- | `ios_tap` | Tap at specific coordinates on screen |
177
- | `ios_tap_element` | Tap an element by its accessibility label |
178
- | `ios_swipe` | Swipe from one point to another |
179
- | `ios_input_text` | Type text into the active input field |
180
- | `ios_button` | Press hardware buttons (HOME, LOCK, SIRI, etc.) |
181
- | `ios_key_event` | Send a key event by keycode |
182
- | `ios_key_sequence` | Send multiple key events in sequence |
183
- | `ios_describe_all` | Get accessibility tree for entire screen |
184
- | `ios_describe_point`| Get accessibility info for element at specific point |
154
+ | Tool | Description |
155
+ | ----------------------- | --------------------------------------------------------------- |
156
+ | `list_ios_simulators` | List available iOS simulators |
157
+ | `ios_screenshot` | Take a screenshot from an iOS simulator |
158
+ | `ios_install_app` | Install an app bundle (.app) on a simulator |
159
+ | `ios_launch_app` | Launch an app by bundle ID |
160
+ | `ios_open_url` | Open a URL (deep links or web URLs) |
161
+ | `ios_terminate_app` | Terminate a running app |
162
+ | `ios_boot_simulator` | Boot a simulator by UDID |
163
+ | `ios_find_element` | Find element by label/value (requires IDB, no screenshot) |
164
+ | `ios_wait_for_element` | Wait for element to appear (requires IDB) |
185
165
 
186
166
  ## Usage
187
167
 
@@ -411,20 +391,6 @@ Set `awaitPromise=false` for synchronous execution only.
411
391
 
412
392
  ## Device Interaction
413
393
 
414
- > **💡 Best Practice: Use Element-Based Tapping**
415
- >
416
- > Prefer `android_tap_element` and `ios_tap_element` over coordinate-based tapping (`android_tap`, `ios_tap`).
417
- >
418
- > **Why?**
419
- > - More reliable - elements are found by text/label, not fragile coordinates
420
- > - Self-documenting - `tap_element(text="Settings")` is clearer than `tap(x=540, y=1200)`
421
- > - Resolution-independent - works across different screen sizes
422
- >
423
- > **Workflow:**
424
- > 1. Use `android_describe_all` or `ios_describe_all` to see available elements
425
- > 2. Use `android_tap_element` or `ios_tap_element` to interact by text/label
426
- > 3. Fall back to coordinate-based tapping only when elements lack accessible text
427
-
428
394
  ### Android (requires ADB)
429
395
 
430
396
  List connected devices:
@@ -466,53 +432,6 @@ android_key_event with key="HOME"
466
432
  android_key_event with key="ENTER"
467
433
  ```
468
434
 
469
- ### Android UI Automation (Accessibility)
470
-
471
- Get the full UI hierarchy:
472
-
473
- ```
474
- android_describe_all
475
- ```
476
-
477
- Example output:
478
- ```
479
- [FrameLayout] frame=(0, 0, 1080x2340) tap=(540, 1170)
480
- [LinearLayout] frame=(0, 63, 1080x147) tap=(540, 136)
481
- [TextView] "Settings" frame=(48, 77, 200x63) tap=(148, 108)
482
- [RecyclerView] frame=(0, 210, 1080x2130) tap=(540, 1275)
483
- [Button] "Save" frame=(800, 2200, 200x80) tap=(900, 2240)
484
- ```
485
-
486
- Get element info at coordinates:
487
-
488
- ```
489
- android_describe_point with x=540 y=1170
490
- ```
491
-
492
- Tap an element by text:
493
-
494
- ```
495
- android_tap_element with text="Settings"
496
- ```
497
-
498
- Tap using partial text match:
499
-
500
- ```
501
- android_tap_element with textContains="Save"
502
- ```
503
-
504
- Tap by resource ID:
505
-
506
- ```
507
- android_tap_element with resourceId="save_button"
508
- ```
509
-
510
- Tap by content description:
511
-
512
- ```
513
- android_tap_element with contentDesc="Navigate up"
514
- ```
515
-
516
435
  ### iOS Simulator (requires Xcode)
517
436
 
518
437
  List available simulators:
@@ -545,78 +464,91 @@ Open a deep link:
545
464
  ios_open_url with url="myapp://settings"
546
465
  ```
547
466
 
548
- ### iOS UI Automation (requires IDB)
549
-
550
- Install IDB first: `brew install idb-companion`
467
+ ## Efficient UI Automation (No Screenshots)
551
468
 
552
- **Important: Coordinate System**
553
- - iOS IDB uses **points** (logical coordinates), not pixels
554
- - For 2x Retina displays: 1 point = 2 pixels
555
- - Example: 1640x2360 pixel screenshot = 820x1180 points
556
- - Use `ios_describe_all` to get exact element coordinates in points
469
+ For action triggering without layout debugging, use element-based tools instead of screenshots. This is **2-3x faster** and uses fewer tokens.
557
470
 
558
- Tap on screen (coordinates in points):
471
+ ### Android - Find and Tap by Text
559
472
 
560
473
  ```
561
- ios_tap with x=200 y=400
562
- ```
474
+ # Wait for screen to load
475
+ android_wait_for_element with text="Login"
563
476
 
564
- Long press (hold for 2 seconds):
477
+ # Find element (returns tap coordinates)
478
+ android_find_element with textContains="submit"
565
479
 
566
- ```
567
- ios_tap with x=200 y=400 duration=2
480
+ # Tap the element (use coordinates from find_element)
481
+ android_tap with x=540 y=960
568
482
  ```
569
483
 
570
- Swipe gesture:
484
+ Search options:
485
+ - `text` - exact text match
486
+ - `textContains` - partial text (case-insensitive)
487
+ - `contentDesc` - accessibility content description
488
+ - `contentDescContains` - partial content description
489
+ - `resourceId` - resource ID (e.g., "button" or "com.app:id/button")
571
490
 
572
- ```
573
- ios_swipe with startX=200 startY=600 endX=200 endY=200
574
- ```
491
+ ### iOS - Find and Tap by Label (requires IDB)
575
492
 
576
- Type text (tap input field first):
577
-
578
- ```
579
- ios_tap with x=200 y=300
580
- ios_input_text with text="hello@example.com"
493
+ ```bash
494
+ # Install IDB first
495
+ brew install idb-companion
581
496
  ```
582
497
 
583
- Press hardware buttons:
584
-
585
498
  ```
586
- ios_button with button="HOME"
587
- ios_button with button="LOCK"
588
- ios_button with button="SIRI"
499
+ # Wait for element
500
+ ios_wait_for_element with label="Sign In"
501
+
502
+ # Find element by partial label
503
+ ios_find_element with labelContains="welcome"
589
504
  ```
590
505
 
591
- Get accessibility info for the screen:
506
+ Search options:
507
+ - `label` - exact accessibility label
508
+ - `labelContains` - partial label (case-insensitive)
509
+ - `value` - accessibility value
510
+ - `valueContains` - partial value
511
+ - `type` - element type (e.g., "Button", "TextField")
592
512
 
593
- ```
594
- ios_describe_all
595
- ```
513
+ ### Wait for Screen Transitions
596
514
 
597
- Get accessibility info at a specific point:
515
+ Both platforms support waiting with timeout:
598
516
 
599
517
  ```
600
- ios_describe_point with x=200 y=400
518
+ android_wait_for_element with text="Dashboard" timeoutMs=15000 pollIntervalMs=500
519
+ ios_wait_for_element with label="Home" timeoutMs=10000
601
520
  ```
602
521
 
603
- Tap an element by accessibility label:
522
+ ### Recommended Workflow (Priority Order)
604
523
 
605
- ```
606
- ios_tap_element with label="Settings"
607
- ```
524
+ **Always try accessibility tools first, fall back to screenshots only when needed:**
608
525
 
609
- Tap using partial label match:
526
+ 1. **Wait for screen** → Use `wait_for_element` with expected text/label
527
+ 2. **Find target** → Use `find_element` to get tap coordinates
528
+ 3. **Tap** → Use `tap` with coordinates from step 2
529
+ 4. **Fallback** → If element not in accessibility tree, use `screenshot`
610
530
 
611
531
  ```
612
- ios_tap_element with labelContains="Sign"
532
+ # Example: Tap "Submit" button after screen loads
533
+ android_wait_for_element with text="Submit" # Step 1: Wait
534
+ android_find_element with text="Submit" # Step 2: Find (returns center coordinates)
535
+ android_tap with x=540 y=1200 # Step 3: Tap (use returned coordinates)
613
536
  ```
614
537
 
615
- When multiple elements match, use index (0-based):
538
+ **Why this order?**
539
+ - `find_element`: ~100-200 tokens, <100ms
540
+ - `screenshot`: ~400-500 tokens, 200-500ms
616
541
 
617
- ```
618
- ios_tap_element with labelContains="Button" index=1
619
- ```
542
+ ### When to Use Screenshots vs Element Tools
543
+
544
+ | Use Case | Recommended Tool |
545
+ |----------|------------------|
546
+ | Trigger button taps | `find_element` + `tap` |
547
+ | Wait for screen load | `wait_for_element` |
548
+ | Navigate through flow | `wait_for_element` + `tap` |
549
+ | Debug layout issues | `screenshot` |
550
+ | Verify visual appearance | `screenshot` |
551
+ | Find elements without labels | `screenshot` |
620
552
 
621
553
  ## Supported React Native Versions
622
554
 
@@ -96,6 +96,81 @@ export declare function androidInputText(text: string, deviceId?: string): Promi
96
96
  * Send a key event to an Android device
97
97
  */
98
98
  export declare function androidKeyEvent(keyCode: number | keyof typeof ANDROID_KEY_EVENTS, deviceId?: string): Promise<AdbResult>;
99
+ /**
100
+ * UI Element from accessibility tree
101
+ */
102
+ export interface AndroidUIElement {
103
+ text: string;
104
+ contentDesc: string;
105
+ resourceId: string;
106
+ className: string;
107
+ bounds: {
108
+ left: number;
109
+ top: number;
110
+ right: number;
111
+ bottom: number;
112
+ width: number;
113
+ height: number;
114
+ };
115
+ center: {
116
+ x: number;
117
+ y: number;
118
+ };
119
+ clickable: boolean;
120
+ enabled: boolean;
121
+ focused: boolean;
122
+ scrollable: boolean;
123
+ selected: boolean;
124
+ }
125
+ /**
126
+ * Result of element find operations
127
+ */
128
+ export interface FindElementResult {
129
+ success: boolean;
130
+ found: boolean;
131
+ element?: AndroidUIElement;
132
+ allMatches?: AndroidUIElement[];
133
+ matchCount?: number;
134
+ error?: string;
135
+ }
136
+ /**
137
+ * Result of wait for element operations
138
+ */
139
+ export interface WaitForElementResult extends FindElementResult {
140
+ elapsedMs?: number;
141
+ timedOut?: boolean;
142
+ }
143
+ /**
144
+ * Options for finding elements
145
+ */
146
+ export interface FindElementOptions {
147
+ text?: string;
148
+ textContains?: string;
149
+ contentDesc?: string;
150
+ contentDescContains?: string;
151
+ resourceId?: string;
152
+ index?: number;
153
+ }
154
+ /**
155
+ * Get UI accessibility tree from Android device using uiautomator
156
+ */
157
+ export declare function androidGetUITree(deviceId?: string): Promise<{
158
+ success: boolean;
159
+ elements?: AndroidUIElement[];
160
+ rawXml?: string;
161
+ error?: string;
162
+ }>;
163
+ /**
164
+ * Find element(s) in the UI tree matching the given criteria
165
+ */
166
+ export declare function androidFindElement(options: FindElementOptions, deviceId?: string): Promise<FindElementResult>;
167
+ /**
168
+ * Wait for element to appear on screen with polling
169
+ */
170
+ export declare function androidWaitForElement(options: FindElementOptions & {
171
+ timeoutMs?: number;
172
+ pollIntervalMs?: number;
173
+ }, deviceId?: string): Promise<WaitForElementResult>;
99
174
  /**
100
175
  * Get device screen size
101
176
  */
@@ -1 +1 @@
1
- {"version":3,"file":"android.d.ts","sourceRoot":"","sources":["../../src/core/android.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"android.d.ts","sourceRoot":"","sources":["../../src/core/android.ts"],"names":[],"mappings":"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"}
@@ -641,6 +641,245 @@ export async function androidKeyEvent(keyCode, deviceId) {
641
641
  };
642
642
  }
643
643
  }
644
+ /**
645
+ * Parse bounds string like "[0,0][1080,1920]" to AndroidUIElement bounds
646
+ */
647
+ function parseBoundsForUIElement(boundsStr) {
648
+ const match = boundsStr.match(/\[(\d+),(\d+)\]\[(\d+),(\d+)\]/);
649
+ if (!match)
650
+ return null;
651
+ const left = parseInt(match[1], 10);
652
+ const top = parseInt(match[2], 10);
653
+ const right = parseInt(match[3], 10);
654
+ const bottom = parseInt(match[4], 10);
655
+ return {
656
+ left,
657
+ top,
658
+ right,
659
+ bottom,
660
+ width: right - left,
661
+ height: bottom - top
662
+ };
663
+ }
664
+ /**
665
+ * Parse uiautomator XML dump into element array
666
+ */
667
+ function parseUIAutomatorXML(xml) {
668
+ const elements = [];
669
+ // Match all node elements with their attributes
670
+ const nodeRegex = /<node\s+([^>]+)\/?>|<node\s+([^>]+)>/g;
671
+ let match;
672
+ while ((match = nodeRegex.exec(xml)) !== null) {
673
+ const attrStr = match[1] || match[2];
674
+ if (!attrStr)
675
+ continue;
676
+ // Extract attributes
677
+ const getAttr = (name) => {
678
+ const attrMatch = attrStr.match(new RegExp(`${name}="([^"]*)"`));
679
+ return attrMatch ? attrMatch[1] : "";
680
+ };
681
+ const boundsStr = getAttr("bounds");
682
+ const bounds = parseBoundsForUIElement(boundsStr);
683
+ if (!bounds)
684
+ continue;
685
+ const element = {
686
+ text: getAttr("text"),
687
+ contentDesc: getAttr("content-desc"),
688
+ resourceId: getAttr("resource-id"),
689
+ className: getAttr("class"),
690
+ bounds,
691
+ center: {
692
+ x: Math.round((bounds.left + bounds.right) / 2),
693
+ y: Math.round((bounds.top + bounds.bottom) / 2)
694
+ },
695
+ clickable: getAttr("clickable") === "true",
696
+ enabled: getAttr("enabled") === "true",
697
+ focused: getAttr("focused") === "true",
698
+ scrollable: getAttr("scrollable") === "true",
699
+ selected: getAttr("selected") === "true"
700
+ };
701
+ elements.push(element);
702
+ }
703
+ return elements;
704
+ }
705
+ /**
706
+ * Match element against find options
707
+ */
708
+ function matchesElement(element, options) {
709
+ if (options.text !== undefined) {
710
+ if (element.text !== options.text)
711
+ return false;
712
+ }
713
+ if (options.textContains !== undefined) {
714
+ if (!element.text.toLowerCase().includes(options.textContains.toLowerCase()))
715
+ return false;
716
+ }
717
+ if (options.contentDesc !== undefined) {
718
+ if (element.contentDesc !== options.contentDesc)
719
+ return false;
720
+ }
721
+ if (options.contentDescContains !== undefined) {
722
+ if (!element.contentDesc.toLowerCase().includes(options.contentDescContains.toLowerCase()))
723
+ return false;
724
+ }
725
+ if (options.resourceId !== undefined) {
726
+ // Support both full "com.app:id/button" and short "button" forms
727
+ const shortId = element.resourceId.split("/").pop() || "";
728
+ if (element.resourceId !== options.resourceId && shortId !== options.resourceId)
729
+ return false;
730
+ }
731
+ return true;
732
+ }
733
+ /**
734
+ * Get UI accessibility tree from Android device using uiautomator
735
+ */
736
+ export async function androidGetUITree(deviceId) {
737
+ try {
738
+ const adbAvailable = await isAdbAvailable();
739
+ if (!adbAvailable) {
740
+ return {
741
+ success: false,
742
+ error: "ADB is not installed or not in PATH. Install Android SDK Platform Tools."
743
+ };
744
+ }
745
+ const deviceArg = buildDeviceArg(deviceId);
746
+ const device = deviceId || (await getDefaultAndroidDevice());
747
+ if (!device) {
748
+ return {
749
+ success: false,
750
+ error: "No Android device connected. Connect a device or start an emulator."
751
+ };
752
+ }
753
+ // Dump UI hierarchy to device
754
+ const remotePath = "/sdcard/ui_dump.xml";
755
+ await execAsync(`adb ${deviceArg} shell uiautomator dump ${remotePath}`, {
756
+ timeout: ADB_TIMEOUT
757
+ });
758
+ // Read the XML content
759
+ const { stdout } = await execAsync(`adb ${deviceArg} shell cat ${remotePath}`, {
760
+ timeout: ADB_TIMEOUT
761
+ });
762
+ // Clean up remote file
763
+ await execAsync(`adb ${deviceArg} shell rm ${remotePath}`, {
764
+ timeout: ADB_TIMEOUT
765
+ }).catch(() => { });
766
+ const elements = parseUIAutomatorXML(stdout);
767
+ return {
768
+ success: true,
769
+ elements,
770
+ rawXml: stdout
771
+ };
772
+ }
773
+ catch (error) {
774
+ return {
775
+ success: false,
776
+ error: `Failed to get UI tree: ${error instanceof Error ? error.message : String(error)}`
777
+ };
778
+ }
779
+ }
780
+ /**
781
+ * Find element(s) in the UI tree matching the given criteria
782
+ */
783
+ export async function androidFindElement(options, deviceId) {
784
+ try {
785
+ // Validate that at least one search criteria is provided
786
+ if (!options.text && !options.textContains && !options.contentDesc &&
787
+ !options.contentDescContains && !options.resourceId) {
788
+ return {
789
+ success: false,
790
+ found: false,
791
+ error: "At least one search criteria (text, textContains, contentDesc, contentDescContains, or resourceId) must be provided"
792
+ };
793
+ }
794
+ const treeResult = await androidGetUITree(deviceId);
795
+ if (!treeResult.success || !treeResult.elements) {
796
+ return {
797
+ success: false,
798
+ found: false,
799
+ error: treeResult.error
800
+ };
801
+ }
802
+ // Find matching elements
803
+ const matches = treeResult.elements.filter(el => matchesElement(el, options));
804
+ if (matches.length === 0) {
805
+ return {
806
+ success: true,
807
+ found: false,
808
+ matchCount: 0
809
+ };
810
+ }
811
+ // Select the element at the specified index (default 0)
812
+ const index = options.index ?? 0;
813
+ const selectedElement = matches[index];
814
+ if (!selectedElement) {
815
+ return {
816
+ success: true,
817
+ found: false,
818
+ matchCount: matches.length,
819
+ error: `Index ${index} out of bounds. Found ${matches.length} matching element(s).`
820
+ };
821
+ }
822
+ return {
823
+ success: true,
824
+ found: true,
825
+ element: selectedElement,
826
+ allMatches: matches,
827
+ matchCount: matches.length
828
+ };
829
+ }
830
+ catch (error) {
831
+ return {
832
+ success: false,
833
+ found: false,
834
+ error: `Failed to find element: ${error instanceof Error ? error.message : String(error)}`
835
+ };
836
+ }
837
+ }
838
+ /**
839
+ * Wait for element to appear on screen with polling
840
+ */
841
+ export async function androidWaitForElement(options, deviceId) {
842
+ const timeoutMs = options.timeoutMs ?? 10000;
843
+ const pollIntervalMs = options.pollIntervalMs ?? 500;
844
+ const startTime = Date.now();
845
+ // Validate that at least one search criteria is provided
846
+ if (!options.text && !options.textContains && !options.contentDesc &&
847
+ !options.contentDescContains && !options.resourceId) {
848
+ return {
849
+ success: false,
850
+ found: false,
851
+ timedOut: false,
852
+ error: "At least one search criteria (text, textContains, contentDesc, contentDescContains, or resourceId) must be provided"
853
+ };
854
+ }
855
+ while (Date.now() - startTime < timeoutMs) {
856
+ const result = await androidFindElement(options, deviceId);
857
+ if (result.found && result.element) {
858
+ return {
859
+ ...result,
860
+ elapsedMs: Date.now() - startTime,
861
+ timedOut: false
862
+ };
863
+ }
864
+ // If there was an error (not just "not found"), return it
865
+ if (!result.success) {
866
+ return {
867
+ ...result,
868
+ elapsedMs: Date.now() - startTime,
869
+ timedOut: false
870
+ };
871
+ }
872
+ // Wait before next poll
873
+ await new Promise(resolve => setTimeout(resolve, pollIntervalMs));
874
+ }
875
+ return {
876
+ success: true,
877
+ found: false,
878
+ elapsedMs: Date.now() - startTime,
879
+ timedOut: true,
880
+ error: `Timed out after ${timeoutMs}ms waiting for element`
881
+ };
882
+ }
644
883
  /**
645
884
  * Get device screen size
646
885
  */