@buoy-gg/highlight-updates 7.0.44 → 7.0.46

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.
@@ -1 +1 @@
1
- "use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.describeScreen=describeScreen;exports.resolveTouchTarget=resolveTouchTarget;exports.tapElement=tapElement;exports.waitFor=waitFor;var _utils=require("@buoy-gg/shared-ui/utils");var _reactNative=require("react-native");var _tapMatch=require("./tapMatch");const HostComponent=5;const OffscreenComponent=22;const INTERNAL_NAMES=new Set(["View","RCTView","ScrollView","RCTScrollView","RCTScrollContentView","SafeAreaView","RCTSafeAreaView","AndroidHorizontalScrollContentView","KeyboardAvoidingView","Pressable","PlatformPressable","TouchableOpacity","TouchableHighlight","TouchableWithoutFeedback","TouchableNativeFeedback","AnimatedComponent","Wrapper"]);function getReactDevToolsHook(){if(typeof global==="undefined")return null;return global.__REACT_DEVTOOLS_GLOBAL_HOOK__??null}let commitCount=0;let commitCounterInstalled=false;function ensureCommitCounter(){if(commitCounterInstalled)return;const e=getReactDevToolsHook();if(!e)return;const t=e.onCommitFiberRoot;e.onCommitFiberRoot=function(...n){commitCount++;return typeof t==="function"?t.apply(e,n):void 0};commitCounterInstalled=true}const TAP_SETTLE_MS=250;function makeChromeCheck(){const e=new Map;return t=>{const n=[];let r=t;let a=0;let s=false;while(r&&a<400){const u=e.get(r);if(u!==void 0){s=u;break}if(getProps(r)?.__buoyDevToolsRoot){s=true;e.set(r,true);break}n.push(r);r=r.return;a++}for(const u of n)e.set(u,s);return s}}function getAllFiberRoots(){const e=getReactDevToolsHook();if(!e)return[];const t=[];try{if(e.renderers&&typeof e.renderers.forEach==="function"){e.renderers.forEach((n,r)=>{try{const a=e.getFiberRoots?.(r);if(a)a.forEach(s=>t.push(s))}catch{}})}if(t.length===0&&e.getFiberRoots){const n=e.getFiberRoots(1);if(n)n.forEach(r=>t.push(r))}}catch{}return t}function isHiddenOffscreen(e){return e.tag===OffscreenComponent&&e.memoizedState!==null}function isInactiveScreen(e){const t=e.memoizedProps?.activityState;if(t===0||t===1)return true;if(e.memoizedProps?.isPageFocused===false)return true;return false}function traverseFiberTree(e,t,n=0,r=new Set){if(!e||r.has(e)||n>500)return;r.add(e);if(isHiddenOffscreen(e)||isInactiveScreen(e)){if(e.sibling)traverseFiberTree(e.sibling,t,n,r);return}t(e);if(e.child)traverseFiberTree(e.child,t,n+1,r);if(e.sibling)traverseFiberTree(e.sibling,t,n,r)}function getPublicInstance(e){const t=(0,_utils.getDOMPublicInstance)(e);if(t)return t;if(!e)return null;const n=e;if(n.canonical?.publicInstance)return n.canonical.publicInstance;if(n.canonical&&typeof n.canonical.measure==="function"){return n.canonical}if(typeof n.measure==="function"||typeof n.measureInWindow==="function"){return n}return null}function getNativeTag(e){const t=(0,_utils.getDOMNodeHandle)(e);if(t!==null)return t;if(e==null)return null;const n=e;if(typeof n.__nativeTag==="number")return n.__nativeTag;if(typeof n._nativeTag==="number")return n._nativeTag;if(typeof n.nativeTag==="number")return n.nativeTag;if(n.canonical){if(typeof n.canonical.__nativeTag==="number")return n.canonical.__nativeTag;if(typeof n.canonical._nativeTag==="number")return n.canonical._nativeTag;if(typeof n.canonical.nativeTag==="number")return n.canonical.nativeTag}return null}function findHostInstance(e){const t=getPublicInstance(e.stateNode);if(t){return{instance:t,nativeTag:getNativeTag(e.stateNode),stateNode:e.stateNode}}const n=[];if(e.child)n.push(e.child);let r=0;while(n.length>0&&r<4e3){r++;const a=n.pop();if(!a)continue;const s=getPublicInstance(a.stateNode);if(s)return{instance:s,nativeTag:getNativeTag(a.stateNode),stateNode:a.stateNode};if(a.child)n.push(a.child);if(a.sibling)n.push(a.sibling)}return null}function findScrollAncestor(e){let t=e;let n=0;while(t&&n++<200){const r=t.stateNode;if(r&&typeof r.scrollTo==="function"){const a=typeof r.getNativeScrollRef==="function"&&r.getNativeScrollRef()||null;const s=typeof r.getInnerViewNode==="function"&&r.getInnerViewNode()||typeof r.getScrollableNode==="function"&&r.getScrollableNode()||r;return{instance:r,nativeRef:a,innerNode:s}}t=t.return}return null}async function scrollIntoView(e,t,n,r){try{if((0,_utils.isDOMElement)(n)){n.scrollIntoView({block:"nearest",inline:"nearest",behavior:"instant"});await new Promise(l=>setTimeout(l,16));return true}const a=findScrollAncestor(e);if(!a)return false;const s=t;let u=null;const o=l=>new Promise(i=>{let f=false;const h=y=>{if(f)return;f=true;i(y)};const g=setTimeout(()=>h(null),600);try{l(y=>{clearTimeout(g);h(y)})}catch{clearTimeout(g);h(null)}});if(a.nativeRef&&typeof s.measureLayout==="function"){u=await o(l=>s.measureLayout(a.nativeRef,(i,f)=>l(f),()=>l(null)))}if(u==null){const l=(0,_reactNative.findNodeHandle)(t);const i=typeof a.innerNode==="number"?a.innerNode:(0,_reactNative.findNodeHandle)(a.innerNode);if(l!=null&&i!=null){u=await o(f=>_reactNative.UIManager.measureLayout(l,i,()=>f(null),(h,g)=>f(g)))}}if(u==null)return false;a.instance.scrollTo({y:Math.max(0,u-r),animated:false});await new Promise(l=>setTimeout(l,120));return true}catch{return false}}function getComponentName(e){const t=e?.type;if(!t)return null;if(typeof t==="string")return t;return t.displayName||t.name||null}function getOwningComponentName(e){let t=e?._debugOwner||e?.return;let n=0;while(t&&n<30){n++;const r=getComponentName(t);if(r&&!INTERNAL_NAMES.has(r)&&typeof t.type!=="string"){return r}t=t.return}return null}function getProps(e){return(0,_utils.getDOMControlProps)(e?.stateNode,e?.memoizedProps??e?.pendingProps??null)}function flattenText(e,t=0){if(typeof e==="string")return e;if(typeof e==="number"&&Number.isFinite(e))return String(e);if(Array.isArray(e)&&t<4){const n=[];for(const r of e){const a=flattenText(r,t+1);if(a)n.push(a)}return n.length>0?n.join(""):void 0}return void 0}function normalizeVisibleText(e){return e.replace(/\s+/g," ").trim()}function getVisibleText(e){if(!e)return void 0;const t=n=>{const r=flattenText(n);if(r===void 0)return void 0;const a=normalizeVisibleText(r);return a?a.slice(0,80):void 0};return t(e.children)??t(e.title)??t(e.label)??t(e.placeholder)??void 0}function collectDescendantText(e,t){const n=[];const r=(s,u)=>{if(!s||u>t||n.length>=4)return;if(s!==e){const l=getProps(s);if(l&&(typeof l.onPress==="function"||typeof l.onLongPress==="function"))return}const o=getVisibleText(getProps(s));if(o&&!n.includes(o))n.push(o);if(s.child)r(s.child,u+1);if(s!==e&&s.sibling)r(s.sibling,u)};if(e.child)r(e.child,1);const a=n.join(" ").trim();return a?a.slice(0,80):void 0}function findPressHandler(e){let t=e;let n=0;let r=null;let a=null;while(t&&n<25&&!(r&&a)){const s=getProps(t);if(s){if(!r&&typeof s.onPress==="function")r=s.onPress;if(!a&&typeof s.onLongPress==="function")a=s.onLongPress}t=t.return;n++}if(r)return{handler:r,kind:"onPress",longPress:a??void 0};return a?{handler:a,kind:"onLongPress"}:null}function measureInstance(e){return new Promise(t=>{let n=false;const r=a=>{if(n)return;n=true;t(a)};setTimeout(()=>r(null),1e3);try{if(typeof e.measureInWindow==="function"){e.measureInWindow((a,s,u,o)=>{if(u>0&&o>0)r({x:a,y:s,width:u,height:o});else r(null)})}else if(typeof e.measure==="function"){e.measure((a,s,u,o,l,i)=>{if(u>0&&o>0&&l!=null&&i!=null){r({x:l,y:i,width:u,height:o})}else{r(null)}})}else{r(null)}}catch{r(null)}})}const SPINNER_HOSTS=new Set(["RCTActivityIndicatorView","AndroidProgressBar"]);function collectCandidates(){const e=getAllFiberRoots();if(e.length===0)return[];const t=[];const n=new Map;const r=new Map;const a=makeChromeCheck();for(const s of e){traverseFiberTree(s.current,u=>{const o=getProps(u);if(!o)return;const l=typeof o.onPress==="function"||typeof o.onLongPress==="function";const i=typeof o.onValueChange==="function";const f=typeof o.onChangeText==="function";const h=o.testID||void 0;const g=o.accessibilityLabel||void 0;const y=o.accessibilityRole||o.role||void 0;let p=getVisibleText(o);const b=y==="button"||y==="link"||y==="menuitem"||y==="tab"||y==="switch"||y==="checkbox";const I=typeof u.type==="string"&&SPINNER_HOSTS.has(u.type)&&o.animating!==false;if(!l&&!i&&!f&&!h&&!g&&!p&&!b&&!I){return}let v=null;let c=null;let d=null;if(i){const _=o.value;const S=typeof _==="number"||typeof o.minimumValue==="number";c={handler:o.onValueChange,kind:S?"slider":"toggle",current:_,min:typeof o.minimumValue==="number"?o.minimumValue:void 0,max:typeof o.maximumValue==="number"?o.maximumValue:void 0}}else if(f){d={handler:o.onChangeText,current:typeof o.value==="string"?o.value:void 0}}else{v=findPressHandler(u)}const w=getComponentName(u);let P=w??"Unknown";if(!w||INTERNAL_NAMES.has(w)||typeof u.type==="string"){P=getOwningComponentName(u)??w??"Unknown"}const T=findHostInstance(u);if(!T&&!h)return;if(!p&&!g&&l){p=collectDescendantText(u,6)}const x=T?.instance??null;const C=T?.nativeTag??null;const m=(x?n.get(x):void 0)??(C!=null?r.get(C):void 0);if(m){if(!m.press&&v)m.press=v;else if(m.press&&v?.longPress&&!m.press.longPress){m.press.longPress=v.longPress}if(!m.valueControl&&c)m.valueControl=c;if(!m.textControl&&d)m.textControl=d;if(!m.testID&&h)m.testID=h;if(!m.label&&g)m.label=g;if(!m.text&&p)m.text=p;if(x&&!n.has(x))n.set(x,m);if(C!=null&&!r.has(C))r.set(C,m);return}const D={nativeTag:C,name:P,role:y??(I?"progressbar":void 0),testID:h,label:g,text:p,instance:x,stateNode:T?.stateNode??null,fiber:u,press:v,valueControl:c,textControl:d,rect:null,buoy:a(u)};t.push(D);if(x)n.set(x,D);if(C!=null)r.set(C,D)})}return t}function explicitlyTargets(e,t){if(t.nativeTag!=null)return e.nativeTag===t.nativeTag;if(t.testID&&e.testID)return e.testID.toLowerCase()===t.testID.toLowerCase();return false}function appCandidates(e){const t=collectCandidates();if(e.includeBuoy)return t;return t.filter(n=>!n.buoy||explicitlyTargets(n,e))}async function measureCandidates(e){await Promise.all(e.map(async t=>{if(t.instance)t.rect=await measureInstance(t.instance)}))}async function describeScreen(e={}){const t=collectCandidates();const n=e.includeBuoy?t:t.filter(l=>!l.buoy);const r=t.length-n.length;await measureCandidates(n);const a=_reactNative.Dimensions.get("window");const s=Math.round(a.width)||1;const u=Math.round(a.height)||1;const o=[];for(const l of n){const i=l.rect;if(!i&&!l.testID)continue;let f;let h;if(i){const p=i.x+i.width/2;const b=i.y+i.height/2;if(b<-u*.1||b>u*1.1||p<-s*.1||p>s*1.1){continue}f={x:round2(p/s),y:round2(b/u)};h={x:round2(i.x/s),y:round2(i.y/u),width:round2(i.width/s),height:round2(i.height/u)}}const g=l.valueControl?l.valueControl.kind:l.textControl?"text":void 0;const y=l.valueControl?l.valueControl.current:l.textControl?.current;o.push({nativeTag:l.nativeTag,name:l.name,role:l.role,testID:l.testID,label:l.label,text:l.text,interactive:!!(l.press||l.valueControl||l.textControl),longPressable:l.press&&(l.press.kind==="onLongPress"||l.press.longPress)?true:void 0,control:g,value:y,tap:f,frame:h})}o.sort((l,i)=>{const f=l.tap?.y??2;const h=i.tap?.y??2;if(Math.abs(f-h)>.02)return f-h;return(l.tap?.x??2)-(i.tap?.x??2)});return{screen:{width:s,height:u},count:o.length,elements:o,...r>0?{hiddenBuoy:r}:{}}}function round2(e){return Math.round(e*100)/100}function fieldsOf(e){return{nativeTag:e.nativeTag,testID:e.testID,label:e.label,text:e.text,name:e.name}}function matchScore(e,t){return(0,_tapMatch.scoreTapMatch)(fieldsOf(e),t)}function tapPoint(e,t,n){if(!e||!t||!n)return void 0;return{x:(e.x+e.width/2)/t,y:(e.y+e.height/2)/n}}function makeSyntheticPressEvent(e){const t=e?e.x+e.width/2:0;const n=e?e.y+e.height/2:0;const r={locationX:e?e.width/2:0,locationY:e?e.height/2:0,pageX:t,pageY:n,timestamp:0,target:null,identifier:1,touches:[],changedTouches:[]};return{nativeEvent:r,persist:()=>{},preventDefault:()=>{},stopPropagation:()=>{},isDefaultPrevented:()=>false,isPropagationStopped:()=>false,target:null,currentTarget:null,type:"press",timeStamp:0}}async function tapElement(e){const t=appCandidates(e);if(t.length===0){return{tapped:false,reason:"No React fiber roots / elements found on screen."}}const n=t.map(c=>({c,score:matchScore(c,e)})).filter(c=>c.score>0).sort((c,d)=>d.score-c.score);const r=c=>!!(c.press||c.valueControl||c.textControl);if(n.length===0){return{tapped:false,reason:"No element matched. Call describe_screen to see available elements (match by testID, text, or name).",candidates:t.filter(r).slice(0,12).map(c=>({nativeTag:c.nativeTag,name:c.name,testID:c.testID,text:c.text}))}}const a=_reactNative.Dimensions.get("window");const s=a.width||1;const u=a.height||1;const o=n.slice(0,15);await Promise.all(o.map(async c=>{if(c.c.instance&&!c.c.rect)c.c.rect=await measureInstance(c.c.instance)}));for(const c of o){c.score=(0,_tapMatch.scoreTapMatch)({...fieldsOf(c.c),tap:tapPoint(c.c.rect,s,u)},e)}o.sort((c,d)=>d.score-c.score);const l=o.find(c=>r(c.c))??n.find(c=>r(c.c))??o[0];const i=l.c;const f=(c,d)=>c.press&&c.press.handler===d.press?.handler||c.valueControl&&c.valueControl.handler===d.valueControl?.handler||c.textControl&&c.textControl.handler===d.textControl?.handler;const h=o.filter(c=>c!==l&&r(c.c)&&c.score===l.score).filter(c=>!f(c.c,i)).filter((c,d,w)=>w.findIndex(P=>f(P.c,c.c))===d).slice(0,3).map(c=>({nativeTag:c.c.nativeTag,name:c.c.name,testID:c.c.testID,text:c.c.text,tap:tapPoint(c.c.rect,s,u)}));if(!r(i)){return{tapped:false,reason:`Matched "${i.name}"${i.text?` ("${i.text}")`:""} but it has no onPress / onValueChange / onChangeText handler (e.g. a react-native-gesture-handler GestureDetector). This element can't be driven in JS.`,matched:{nativeTag:i.nativeTag,name:i.name,testID:i.testID,label:i.label,text:i.text,via:"onPress"}}}if(i.instance&&!i.rect)i.rect=await measureInstance(i.instance);let g=false;const y=i.rect==null||!(0,_tapMatch.isRectCenterOnScreen)(i.rect,s,u);if(y&&e.scrollIntoView!==false&&i.instance){g=await scrollIntoView(i.fiber,i.instance,i.stateNode,80);if(g)i.rect=await measureInstance(i.instance)}const p={nativeTag:i.nativeTag,name:i.name,testID:i.testID,label:i.label,text:i.text};ensureCommitCounter();const b=commitCount;const I=Date.now();const v=async c=>{await new Promise(d=>setTimeout(d,TAP_SETTLE_MS));return{tapped:true,scrolled:g,matched:c,effect:{commits:commitCount-b,observedMs:Date.now()-I},...h.length?{alsoMatched:h}:{}}};try{if(i.press){if(e.longPress){const d=i.press.kind==="onLongPress"?i.press.handler:i.press.longPress;if(!d){return{tapped:false,scrolled:g,reason:`Matched "${i.name}"${i.text?` ("${i.text}")`:""} but it has no onLongPress handler \u2014 call tap_element without longPress to tap it normally.`,matched:{...p,via:"onPress"}}}d(makeSyntheticPressEvent(i.rect));return await v({...p,via:"onLongPress"})}i.press.handler(makeSyntheticPressEvent(i.rect));return await v({...p,via:i.press.kind})}if(i.valueControl){const d=i.valueControl;let w;if(d.kind==="toggle"){w=typeof e.value==="boolean"?e.value:!d.current}else{const P=d.min??0;const T=d.max??1;if(typeof e.value==="number"){w=Math.min(T,Math.max(P,e.value))}else{w=d.current===T?P:T}}d.handler(w);return await v({...p,via:"onValueChange",value:w})}const c=e.text??(typeof e.value==="string"?e.value:"");i.textControl.handler(c);return await v({...p,via:"onChangeText",value:c})}catch(c){return{tapped:false,scrolled:g,reason:`handler threw: ${c instanceof Error?c.message:String(c)}`,matched:{...p,via:i.press?i.press.kind:i.valueControl?"onValueChange":"onChangeText"}}}}const WAIT_DEFAULT_MS=5e3;const WAIT_MAX_MS=3e4;const WAIT_MIN_POLL_MS=50;const WAIT_DEFAULT_POLL_MS=150;async function waitFor(e){const t=e.nativeTag!=null||!!String(e.testID??"").trim()||!!String(e.query??"").trim();if(!t){return{ok:false,waitedMs:0,polls:0,reason:"Nothing to wait for \u2014 pass nativeTag, testID or query."}}const n=Math.min(Math.max(e.timeoutMs??WAIT_DEFAULT_MS,0),WAIT_MAX_MS);const r=Math.max(e.pollMs??WAIT_DEFAULT_POLL_MS,WAIT_MIN_POLL_MS);const a=e.gone===true;const s=Date.now();let u=0;for(;;){u+=1;const{hit:o,closest:l}=await findOnScreen(e);if(a?!o:!!o){return{ok:true,waitedMs:Date.now()-s,polls:u,matched:o??void 0}}if(Date.now()-s>=n){const i=l?.length?l:void 0;const f=a?`Still on screen after ${n}ms.`:i?`Not on screen after ${n}ms \u2014 ${i.length===1?"one element":`${i.length} elements`} matched but ${i.some(h=>h.onScreen)?"not well enough":"none is visible"}. See \`closest\`; do not retry with different words, call describeScreen.`:`Not on screen after ${n}ms, and nothing matched those words at all. Call describeScreen to see what is there.`;return{ok:false,waitedMs:Date.now()-s,polls:u,reason:f,matched:o??void 0,closest:i}}await new Promise(i=>setTimeout(i,r))}}async function findOnScreen(e){const t=appCandidates(e).map(o=>({c:o,score:matchScore(o,e)})).filter(o=>o.score>0).sort((o,l)=>l.score-o.score).slice(0,8);if(t.length===0)return{hit:null,closest:void 0};await measureCandidates(t.map(o=>o.c));const n=_reactNative.Dimensions.get("window");const r=Math.round(n.width)||1;const a=Math.round(n.height)||1;let s=null;for(const{c:o}of t){if(!(0,_tapMatch.isRectCenterOnScreen)(o.rect,r,a))continue;s={nativeTag:o.nativeTag,name:o.name,testID:o.testID,label:o.label,text:o.text,interactive:!!(o.press||o.valueControl||o.textControl)};break}const u=t.slice(0,3).map(({c:o})=>({name:o.name,testID:o.testID,text:o.text,onScreen:(0,_tapMatch.isRectCenterOnScreen)(o.rect,r,a)}));return{hit:s,closest:u}}function isBuoyChrome(e){let t=e;let n=0;while(t&&n<400){if(getProps(t)?.__buoyDevToolsRoot)return true;t=t.return;n++}return false}function preferHandlerOwner(e,t){const n=getProps(e.fiber);if(typeof n?.onPress==="function"||typeof n?.onValueChange==="function"||typeof n?.onChangeText==="function"){return e}let r=null;let a=e.fiber?.return;let s=0;while(a&&s<25){if(typeof getProps(a)?.onPress==="function"){r=a;break}a=a.return;s++}if(!r)return e;let u=r;for(let o=0;o<6&&u;o++){const l=t.get(u);if(l)return l;u=u.return}return e}function resolveTouchTarget(e){const t=collectCandidates();if(t.length===0)return null;const n=new Map;for(const o of t)n.set(o.fiber,o);const r=t.find(o=>o.nativeTag===e);let a=null;if(!r){for(const o of getAllFiberRoots()){traverseFiberTree(o.current,l=>{if(a)return;if(l.stateNode&&getNativeTag(l.stateNode)===e){a=l}});if(a)break}}let s=r??null;if(!s&&a){let o=a;let l=0;let i=null;while(o&&l<25){const f=n.get(o);if(f){if(f.press||f.valueControl||f.textControl){s=f;break}if(!i)i=f}o=o.return;l++}if(!s)s=i}if(!s)return null;s=preferHandlerOwner(s,n);if(!s.press&&!s.valueControl&&!s.textControl)return null;if(isBuoyChrome(s.fiber))return null;const u=s.valueControl?s.valueControl.kind:s.textControl?"text":"press";return{nativeTag:s.nativeTag,name:s.name,role:s.role,testID:s.testID,label:s.label,text:s.text,control:u,value:s.valueControl?s.valueControl.current:s.textControl?s.textControl.current:void 0}}
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.describeScreen=describeScreen;exports.resolveTouchTarget=resolveTouchTarget;exports.tapElement=tapElement;exports.waitFor=waitFor;var _utils=require("@buoy-gg/shared-ui/utils");var _reactNative=require("react-native");var _tapMatch=require("./tapMatch");const HostComponent=5;const OffscreenComponent=22;const INTERNAL_NAMES=new Set(["View","RCTView","ScrollView","RCTScrollView","RCTScrollContentView","SafeAreaView","RCTSafeAreaView","AndroidHorizontalScrollContentView","KeyboardAvoidingView","Pressable","PlatformPressable","TouchableOpacity","TouchableHighlight","TouchableWithoutFeedback","TouchableNativeFeedback","AnimatedComponent","Wrapper"]);function getReactDevToolsHook(){if(typeof global==="undefined")return null;return global.__REACT_DEVTOOLS_GLOBAL_HOOK__??null}let commitCount=0;let commitCounterInstalled=false;function ensureCommitCounter(){if(commitCounterInstalled)return;const e=getReactDevToolsHook();if(!e)return;const t=e.onCommitFiberRoot;e.onCommitFiberRoot=function(...n){commitCount++;return typeof t==="function"?t.apply(e,n):void 0};commitCounterInstalled=true}const TAP_SETTLE_MS=250;function makeChromeCheck(){const e=new Map;return t=>{const n=[];let i=t;let l=0;let s=false;while(i&&l<400){const a=e.get(i);if(a!==void 0){s=a;break}if(getProps(i)?.__buoyDevToolsRoot){s=true;e.set(i,true);break}n.push(i);i=i.return;l++}for(const a of n)e.set(a,s);return s}}function getAllFiberRoots(){const e=getReactDevToolsHook();if(!e)return[];const t=[];try{if(e.renderers&&typeof e.renderers.forEach==="function"){e.renderers.forEach((n,i)=>{try{const l=e.getFiberRoots?.(i);if(l)l.forEach(s=>t.push(s))}catch{}})}if(t.length===0&&e.getFiberRoots){const n=e.getFiberRoots(1);if(n)n.forEach(i=>t.push(i))}}catch{}return t}function isHiddenOffscreen(e){return e.tag===OffscreenComponent&&e.memoizedState!==null}function isInactiveScreen(e){const t=e.memoizedProps?.activityState;if(t===0||t===1)return true;if(e.memoizedProps?.isPageFocused===false)return true;return false}function traverseFiberTree(e,t,n=0,i=new Set){if(!e||i.has(e)||n>500)return;i.add(e);if(isHiddenOffscreen(e)||isInactiveScreen(e)){if(e.sibling)traverseFiberTree(e.sibling,t,n,i);return}t(e);if(e.child)traverseFiberTree(e.child,t,n+1,i);if(e.sibling)traverseFiberTree(e.sibling,t,n,i)}function getPublicInstance(e){const t=(0,_utils.getDOMPublicInstance)(e);if(t)return t;if(!e)return null;const n=e;if(n.canonical?.publicInstance)return n.canonical.publicInstance;if(n.canonical&&typeof n.canonical.measure==="function"){return n.canonical}if(typeof n.measure==="function"||typeof n.measureInWindow==="function"){return n}return null}function getNativeTag(e){const t=(0,_utils.getDOMNodeHandle)(e);if(t!==null)return t;if(e==null)return null;const n=e;if(typeof n.__nativeTag==="number")return n.__nativeTag;if(typeof n._nativeTag==="number")return n._nativeTag;if(typeof n.nativeTag==="number")return n.nativeTag;if(n.canonical){if(typeof n.canonical.__nativeTag==="number")return n.canonical.__nativeTag;if(typeof n.canonical._nativeTag==="number")return n.canonical._nativeTag;if(typeof n.canonical.nativeTag==="number")return n.canonical.nativeTag}return null}function findHostInstance(e){const t=getPublicInstance(e.stateNode);if(t){return{instance:t,nativeTag:getNativeTag(e.stateNode),stateNode:e.stateNode}}const n=[];if(e.child)n.push(e.child);let i=0;while(n.length>0&&i<4e3){i++;const l=n.pop();if(!l)continue;const s=getPublicInstance(l.stateNode);if(s)return{instance:s,nativeTag:getNativeTag(l.stateNode),stateNode:l.stateNode};if(l.child)n.push(l.child);if(l.sibling)n.push(l.sibling)}return null}function findScrollAncestor(e){let t=e;let n=0;while(t&&n++<200){const i=t.stateNode;if(i&&typeof i.scrollTo==="function"){const l=typeof i.getNativeScrollRef==="function"&&i.getNativeScrollRef()||null;const s=typeof i.getInnerViewNode==="function"&&i.getInnerViewNode()||typeof i.getScrollableNode==="function"&&i.getScrollableNode()||i;return{instance:i,nativeRef:l,innerNode:s}}t=t.return}return null}async function scrollIntoView(e,t,n,i){try{if((0,_utils.isDOMElement)(n)){n.scrollIntoView({block:"nearest",inline:"nearest",behavior:"instant"});await new Promise(u=>setTimeout(u,16));return true}const l=findScrollAncestor(e);if(!l)return false;const s=t;let a=null;const r=u=>new Promise(o=>{let f=false;const d=m=>{if(f)return;f=true;o(m)};const h=setTimeout(()=>d(null),600);try{u(m=>{clearTimeout(h);d(m)})}catch{clearTimeout(h);d(null)}});if(l.nativeRef&&typeof s.measureLayout==="function"){a=await r(u=>s.measureLayout(l.nativeRef,(o,f)=>u(f),()=>u(null)))}if(a==null){const u=(0,_reactNative.findNodeHandle)(t);const o=typeof l.innerNode==="number"?l.innerNode:(0,_reactNative.findNodeHandle)(l.innerNode);if(u!=null&&o!=null){a=await r(f=>_reactNative.UIManager.measureLayout(u,o,()=>f(null),(d,h)=>f(h)))}}if(a==null)return false;l.instance.scrollTo({y:Math.max(0,a-i),animated:false});await new Promise(u=>setTimeout(u,120));return true}catch{return false}}function getComponentName(e){const t=e?.type;if(!t)return null;if(typeof t==="string")return t;return t.displayName||t.name||null}function getOwningComponentName(e){let t=e?._debugOwner||e?.return;let n=0;while(t&&n<30){n++;const i=getComponentName(t);if(i&&!INTERNAL_NAMES.has(i)&&typeof t.type!=="string"){return i}t=t.return}return null}function getProps(e){return(0,_utils.getDOMControlProps)(e?.stateNode,e?.memoizedProps??e?.pendingProps??null)}function flattenText(e,t=0){if(typeof e==="string")return e;if(typeof e==="number"&&Number.isFinite(e))return String(e);if(Array.isArray(e)&&t<4){const n=[];for(const i of e){const l=flattenText(i,t+1);if(l)n.push(l)}return n.length>0?n.join(""):void 0}return void 0}function normalizeVisibleText(e){return e.replace(/\s+/g," ").trim()}function getVisibleText(e){if(!e)return void 0;const t=n=>{const i=flattenText(n);if(i===void 0)return void 0;const l=normalizeVisibleText(i);return l?l.slice(0,80):void 0};return t(e.children)??t(e.title)??t(e.label)??t(e.placeholder)??void 0}function collectDescendantText(e,t){const n=[];const i=(s,a)=>{if(!s||a>t||n.length>=4)return;if(s!==e){const u=getProps(s);if(u&&(typeof u.onPress==="function"||typeof u.onLongPress==="function"))return}const r=getVisibleText(getProps(s));if(r&&!n.includes(r))n.push(r);if(s.child)i(s.child,a+1);if(s!==e&&s.sibling)i(s.sibling,a)};if(e.child)i(e.child,1);const l=n.join(" ").trim();return l?l.slice(0,80):void 0}function findPressHandler(e){let t=e;let n=0;let i=null;let l=null;while(t&&n<25&&!(i&&l)){const s=getProps(t);if(s){if(!i&&typeof s.onPress==="function")i=s.onPress;if(!l&&typeof s.onLongPress==="function")l=s.onLongPress}t=t.return;n++}if(i)return{handler:i,kind:"onPress",longPress:l??void 0};return l?{handler:l,kind:"onLongPress"}:null}function measureInstance(e){return new Promise(t=>{let n=false;const i=l=>{if(n)return;n=true;t(l)};setTimeout(()=>i(null),1e3);try{if(typeof e.measureInWindow==="function"){e.measureInWindow((l,s,a,r)=>{if(a>0&&r>0)i({x:l,y:s,width:a,height:r});else i(null)})}else if(typeof e.measure==="function"){e.measure((l,s,a,r,u,o)=>{if(a>0&&r>0&&u!=null&&o!=null){i({x:u,y:o,width:a,height:r})}else{i(null)}})}else{i(null)}}catch{i(null)}})}const SPINNER_HOSTS=new Set(["RCTActivityIndicatorView","AndroidProgressBar"]);function collectCandidates(){const e=getAllFiberRoots();if(e.length===0)return[];const t=[];const n=new Map;const i=new Map;const l=makeChromeCheck();for(const s of e){traverseFiberTree(s.current,a=>{const r=getProps(a);if(!r)return;const u=typeof r.onPress==="function"||typeof r.onLongPress==="function";const o=typeof r.onValueChange==="function";const f=typeof r.onChangeText==="function";const d=r.testID||void 0;const h=r.accessibilityLabel||void 0;const m=r.accessibilityRole||r.role||void 0;let p=getVisibleText(r);const w=m==="button"||m==="link"||m==="menuitem"||m==="tab"||m==="switch"||m==="checkbox";const T=typeof a.type==="string"&&SPINNER_HOSTS.has(a.type)&&r.animating!==false;if(!u&&!o&&!f&&!d&&!h&&!p&&!w&&!T){return}let x=null;let c=null;let g=null;if(o){const _=r.value;const D=typeof _==="number"||typeof r.minimumValue==="number";c={handler:r.onValueChange,kind:D?"slider":"toggle",current:_,min:typeof r.minimumValue==="number"?r.minimumValue:void 0,max:typeof r.maximumValue==="number"?r.maximumValue:void 0}}else if(f){g={handler:r.onChangeText,current:typeof r.value==="string"?r.value:void 0}}else{x=findPressHandler(a)}const v=getComponentName(a);let I=v??"Unknown";if(!v||INTERNAL_NAMES.has(v)||typeof a.type==="string"){I=getOwningComponentName(a)??v??"Unknown"}const C=findHostInstance(a);if(!C&&!d)return;if(!p&&!h&&u){p=collectDescendantText(a,6)}const b=C?.instance??null;const P=C?.nativeTag??null;const y=(b?n.get(b):void 0)??(P!=null?i.get(P):void 0);if(y){if(!y.press&&x)y.press=x;else if(y.press&&x?.longPress&&!y.press.longPress){y.press.longPress=x.longPress}if(!y.valueControl&&c)y.valueControl=c;if(!y.textControl&&g)y.textControl=g;if(!y.testID&&d)y.testID=d;if(!y.label&&h)y.label=h;if(!y.text&&p)y.text=p;if(b&&!n.has(b))n.set(b,y);if(P!=null&&!i.has(P))i.set(P,y);return}const S={nativeTag:P,name:I,role:m??(T?"progressbar":void 0),testID:d,label:h,text:p,instance:b,stateNode:C?.stateNode??null,fiber:a,press:x,valueControl:c,textControl:g,rect:null,buoy:l(a)};t.push(S);if(b)n.set(b,S);if(P!=null)i.set(P,S)})}return t}function explicitlyTargets(e,t){if(t.nativeTag!=null)return e.nativeTag===t.nativeTag;if(t.testID&&e.testID)return e.testID.toLowerCase()===t.testID.toLowerCase();return false}function appCandidates(e){const t=collectCandidates();if(e.includeBuoy)return t;return t.filter(n=>!n.buoy||explicitlyTargets(n,e))}async function measureCandidates(e){await Promise.all(e.map(async t=>{if(t.instance)t.rect=await measureInstance(t.instance)}))}const LIST_WRAPPERS=/^(FlatList|SectionList|VirtualizedSectionList|VirtualizedList|Animated\(.*\)|AnimatedComponent.*)$/;async function collectLists(e,t){const n=[];const i=makeChromeCheck();for(const s of getAllFiberRoots()){traverseFiberTree(s.current,a=>{if(getComponentName(a)!=="VirtualizedList")return;const r=a.memoizedProps;if(!r||typeof r.getItemCount!=="function")return;if(!e&&i(a))return;let u;try{u=Number(r.getItemCount(r.data))}catch{return}if(!Number.isFinite(u)||u<=0)return;const o=[];const f=(m,p)=>{for(let w=m?.child;w;w=w.sibling){const T=getComponentName(w);if(T==="VirtualizedList")continue;if(T==="CellRenderer"){o.push(w);continue}if(p<40)f(w,p+1)}};f(a,0);let d=a;let h=getOwningComponentName(d);for(let m=0;m<6&&h&&LIST_WRAPPERS.test(h);m++){d=d._debugOwner||d.return;h=getOwningComponentName(d)}n.push({owner:h,items:u,cells:o})})}const l=[];for(const s of n){const a=await Promise.all(s.cells.map(async u=>{const o=findHostInstance(u);return o?measureInstance(o.instance):null}));const r=a.filter(t).length;if(r>0&&s.items>r)l.push({owner:s.owner,items:s.items,listed:r})}return l}async function describeScreen(e={}){const t=collectCandidates();const n=e.includeBuoy?t:t.filter(o=>!o.buoy);const i=t.length-n.length;await measureCandidates(n);const l=_reactNative.Dimensions.get("window");const s=Math.round(l.width)||1;const a=Math.round(l.height)||1;const r=[];for(const o of n){const f=o.rect;if(!f&&!o.testID)continue;let d;let h;if(f){const w=f.x+f.width/2;const T=f.y+f.height/2;if(T<-a*.1||T>a*1.1||w<-s*.1||w>s*1.1){continue}d={x:round2(w/s),y:round2(T/a)};h={x:round2(f.x/s),y:round2(f.y/a),width:round2(f.width/s),height:round2(f.height/a)}}const m=o.valueControl?o.valueControl.kind:o.textControl?"text":void 0;const p=o.valueControl?o.valueControl.current:o.textControl?.current;r.push({nativeTag:o.nativeTag,name:o.name,role:o.role,testID:o.testID,label:o.label,text:o.text,interactive:!!(o.press||o.valueControl||o.textControl),longPressable:o.press&&(o.press.kind==="onLongPress"||o.press.longPress)?true:void 0,control:m,value:p,tap:d,frame:h})}r.sort((o,f)=>{const d=o.tap?.y??2;const h=f.tap?.y??2;if(Math.abs(d-h)>.02)return d-h;return(o.tap?.x??2)-(f.tap?.x??2)});const u=await collectLists(!!e.includeBuoy,o=>{if(!o)return false;const f=o.x+o.width/2;return o.y<=a*1.1&&o.y+o.height>=-a*.1&&f>=-s*.1&&f<=s*1.1});return{screen:{width:s,height:a},count:r.length,elements:r,...i>0?{hiddenBuoy:i}:{},...u.length?{lists:u}:{}}}function round2(e){return Math.round(e*100)/100}function fieldsOf(e){return{nativeTag:e.nativeTag,testID:e.testID,label:e.label,text:e.text,name:e.name}}function matchScore(e,t){return(0,_tapMatch.scoreTapMatch)(fieldsOf(e),t)}function tapPoint(e,t,n){if(!e||!t||!n)return void 0;return{x:(e.x+e.width/2)/t,y:(e.y+e.height/2)/n}}function makeSyntheticPressEvent(e){const t=e?e.x+e.width/2:0;const n=e?e.y+e.height/2:0;const i={locationX:e?e.width/2:0,locationY:e?e.height/2:0,pageX:t,pageY:n,timestamp:0,target:null,identifier:1,touches:[],changedTouches:[]};return{nativeEvent:i,persist:()=>{},preventDefault:()=>{},stopPropagation:()=>{},isDefaultPrevented:()=>false,isPropagationStopped:()=>false,target:null,currentTarget:null,type:"press",timeStamp:0}}async function tapElement(e){const t=appCandidates(e);if(t.length===0){return{tapped:false,reason:"No React fiber roots / elements found on screen."}}const n=t.map(c=>({c,score:matchScore(c,e)})).filter(c=>c.score>0).sort((c,g)=>g.score-c.score);const i=c=>!!(c.press||c.valueControl||c.textControl);if(n.length===0){return{tapped:false,reason:"No element matched. Call describe_screen to see available elements (match by testID, text, or name).",candidates:t.filter(i).slice(0,12).map(c=>({nativeTag:c.nativeTag,name:c.name,testID:c.testID,text:c.text}))}}const l=_reactNative.Dimensions.get("window");const s=l.width||1;const a=l.height||1;const r=n.slice(0,15);await Promise.all(r.map(async c=>{if(c.c.instance&&!c.c.rect)c.c.rect=await measureInstance(c.c.instance)}));for(const c of r){c.score=(0,_tapMatch.scoreTapMatch)({...fieldsOf(c.c),tap:tapPoint(c.c.rect,s,a)},e)}r.sort((c,g)=>g.score-c.score);const u=r.find(c=>i(c.c))??n.find(c=>i(c.c))??r[0];const o=u.c;const f=(c,g)=>c.press&&c.press.handler===g.press?.handler||c.valueControl&&c.valueControl.handler===g.valueControl?.handler||c.textControl&&c.textControl.handler===g.textControl?.handler;const d=r.filter(c=>c!==u&&i(c.c)&&c.score===u.score).filter(c=>!f(c.c,o)).filter((c,g,v)=>v.findIndex(I=>f(I.c,c.c))===g).slice(0,3).map(c=>({nativeTag:c.c.nativeTag,name:c.c.name,testID:c.c.testID,text:c.c.text,tap:tapPoint(c.c.rect,s,a)}));if(!i(o)){return{tapped:false,reason:`Matched "${o.name}"${o.text?` ("${o.text}")`:""} but it has no onPress / onValueChange / onChangeText handler (e.g. a react-native-gesture-handler GestureDetector). This element can't be driven in JS.`,matched:{nativeTag:o.nativeTag,name:o.name,testID:o.testID,label:o.label,text:o.text,via:"onPress"}}}if(o.instance&&!o.rect)o.rect=await measureInstance(o.instance);let h=false;const m=o.rect==null||!(0,_tapMatch.isRectCenterOnScreen)(o.rect,s,a);if(m&&e.scrollIntoView!==false&&o.instance){h=await scrollIntoView(o.fiber,o.instance,o.stateNode,80);if(h)o.rect=await measureInstance(o.instance)}const p={nativeTag:o.nativeTag,name:o.name,testID:o.testID,label:o.label,text:o.text};ensureCommitCounter();const w=commitCount;const T=Date.now();const x=async c=>{await new Promise(g=>setTimeout(g,TAP_SETTLE_MS));return{tapped:true,scrolled:h,matched:c,effect:{commits:commitCount-w,observedMs:Date.now()-T},...d.length?{alsoMatched:d}:{}}};try{if(o.press){if(e.longPress){const g=o.press.kind==="onLongPress"?o.press.handler:o.press.longPress;if(!g){return{tapped:false,scrolled:h,reason:`Matched "${o.name}"${o.text?` ("${o.text}")`:""} but it has no onLongPress handler \u2014 call tap_element without longPress to tap it normally.`,matched:{...p,via:"onPress"}}}g(makeSyntheticPressEvent(o.rect));return await x({...p,via:"onLongPress"})}o.press.handler(makeSyntheticPressEvent(o.rect));return await x({...p,via:o.press.kind})}if(o.valueControl){const g=o.valueControl;let v;if(g.kind==="toggle"){v=typeof e.value==="boolean"?e.value:!g.current}else{const I=g.min??0;const C=g.max??1;if(typeof e.value==="number"){v=Math.min(C,Math.max(I,e.value))}else{v=g.current===C?I:C}}g.handler(v);return await x({...p,via:"onValueChange",value:v})}const c=e.text??(typeof e.value==="string"?e.value:"");o.textControl.handler(c);return await x({...p,via:"onChangeText",value:c})}catch(c){return{tapped:false,scrolled:h,reason:`handler threw: ${c instanceof Error?c.message:String(c)}`,matched:{...p,via:o.press?o.press.kind:o.valueControl?"onValueChange":"onChangeText"}}}}const WAIT_DEFAULT_MS=5e3;const WAIT_MAX_MS=3e4;const WAIT_MIN_POLL_MS=50;const WAIT_DEFAULT_POLL_MS=150;async function waitFor(e){const t=e.nativeTag!=null||!!String(e.testID??"").trim()||!!String(e.query??"").trim();if(!t){return{ok:false,waitedMs:0,polls:0,reason:"Nothing to wait for \u2014 pass nativeTag, testID or query."}}const n=Math.min(Math.max(e.timeoutMs??WAIT_DEFAULT_MS,0),WAIT_MAX_MS);const i=Math.max(e.pollMs??WAIT_DEFAULT_POLL_MS,WAIT_MIN_POLL_MS);const l=e.gone===true;const s=Date.now();let a=0;for(;;){a+=1;const{hit:r,closest:u}=await findOnScreen(e);if(l?!r:!!r){return{ok:true,waitedMs:Date.now()-s,polls:a,matched:r??void 0}}if(Date.now()-s>=n){const o=u?.length?u:void 0;const f=l?`Still on screen after ${n}ms.`:o?`Not on screen after ${n}ms \u2014 ${o.length===1?"one element":`${o.length} elements`} matched but ${o.some(d=>d.onScreen)?"not well enough":"none is visible"}. See \`closest\`; do not retry with different words, call describeScreen.`:`Not on screen after ${n}ms, and nothing matched those words at all. Call describeScreen to see what is there.`;return{ok:false,waitedMs:Date.now()-s,polls:a,reason:f,matched:r??void 0,closest:o}}await new Promise(o=>setTimeout(o,i))}}async function findOnScreen(e){const t=appCandidates(e).map(r=>({c:r,score:matchScore(r,e)})).filter(r=>r.score>0).sort((r,u)=>u.score-r.score).slice(0,8);if(t.length===0)return{hit:null,closest:void 0};await measureCandidates(t.map(r=>r.c));const n=_reactNative.Dimensions.get("window");const i=Math.round(n.width)||1;const l=Math.round(n.height)||1;let s=null;for(const{c:r}of t){if(!(0,_tapMatch.isRectCenterOnScreen)(r.rect,i,l))continue;s={nativeTag:r.nativeTag,name:r.name,testID:r.testID,label:r.label,text:r.text,interactive:!!(r.press||r.valueControl||r.textControl)};break}const a=t.slice(0,3).map(({c:r})=>({name:r.name,testID:r.testID,text:r.text,onScreen:(0,_tapMatch.isRectCenterOnScreen)(r.rect,i,l)}));return{hit:s,closest:a}}function isBuoyChrome(e){let t=e;let n=0;while(t&&n<400){if(getProps(t)?.__buoyDevToolsRoot)return true;t=t.return;n++}return false}function preferHandlerOwner(e,t){const n=getProps(e.fiber);if(typeof n?.onPress==="function"||typeof n?.onValueChange==="function"||typeof n?.onChangeText==="function"){return e}let i=null;let l=e.fiber?.return;let s=0;while(l&&s<25){if(typeof getProps(l)?.onPress==="function"){i=l;break}l=l.return;s++}if(!i)return e;let a=i;for(let r=0;r<6&&a;r++){const u=t.get(a);if(u)return u;a=a.return}return e}function resolveTouchTarget(e){const t=collectCandidates();if(t.length===0)return null;const n=new Map;for(const r of t)n.set(r.fiber,r);const i=t.find(r=>r.nativeTag===e);let l=null;if(!i){for(const r of getAllFiberRoots()){traverseFiberTree(r.current,u=>{if(l)return;if(u.stateNode&&getNativeTag(u.stateNode)===e){l=u}});if(l)break}}let s=i??null;if(!s&&l){let r=l;let u=0;let o=null;while(r&&u<25){const f=n.get(r);if(f){if(f.press||f.valueControl||f.textControl){s=f;break}if(!o)o=f}r=r.return;u++}if(!s)s=o}if(!s)return null;s=preferHandlerOwner(s,n);if(!s.press&&!s.valueControl&&!s.textControl)return null;if(isBuoyChrome(s.fiber))return null;const a=s.valueControl?s.valueControl.kind:s.textControl?"text":"press";return{nativeTag:s.nativeTag,name:s.name,role:s.role,testID:s.testID,label:s.label,text:s.text,control:a,value:s.valueControl?s.valueControl.current:s.textControl?s.textControl.current:void 0}}
@@ -1 +1 @@
1
- "use strict";import{getDOMControlProps as ee,getDOMNodeHandle as te,getDOMPublicInstance as ne,isDOMElement as oe}from"@buoy-gg/shared-ui/utils";import{Dimensions as k,UIManager as re,findNodeHandle as E}from"react-native";import{isRectCenterOnScreen as V,scoreTapMatch as H}from"./tapMatch";const Ne=5;const ie=22;const F=new Set(["View","RCTView","ScrollView","RCTScrollView","RCTScrollContentView","SafeAreaView","RCTSafeAreaView","AndroidHorizontalScrollContentView","KeyboardAvoidingView","Pressable","PlatformPressable","TouchableOpacity","TouchableHighlight","TouchableWithoutFeedback","TouchableNativeFeedback","AnimatedComponent","Wrapper"]);function $(){if(typeof global==="undefined")return null;return global.__REACT_DEVTOOLS_GLOBAL_HOOK__??null}let L=0;let W=false;function se(){if(W)return;const e=$();if(!e)return;const t=e.onCommitFiberRoot;e.onCommitFiberRoot=function(...n){L++;return typeof t==="function"?t.apply(e,n):void 0};W=true}const le=250;function ae(){const e=new Map;return t=>{const n=[];let r=t;let l=0;let s=false;while(r&&l<400){const u=e.get(r);if(u!==void 0){s=u;break}if(b(r)?.__buoyDevToolsRoot){s=true;e.set(r,true);break}n.push(r);r=r.return;l++}for(const u of n)e.set(u,s);return s}}function B(){const e=$();if(!e)return[];const t=[];try{if(e.renderers&&typeof e.renderers.forEach==="function"){e.renderers.forEach((n,r)=>{try{const l=e.getFiberRoots?.(r);if(l)l.forEach(s=>t.push(s))}catch{}})}if(t.length===0&&e.getFiberRoots){const n=e.getFiberRoots(1);if(n)n.forEach(r=>t.push(r))}}catch{}return t}function ce(e){return e.tag===ie&&e.memoizedState!==null}function ue(e){const t=e.memoizedProps?.activityState;if(t===0||t===1)return true;if(e.memoizedProps?.isPageFocused===false)return true;return false}function S(e,t,n=0,r=new Set){if(!e||r.has(e)||n>500)return;r.add(e);if(ce(e)||ue(e)){if(e.sibling)S(e.sibling,t,n,r);return}t(e);if(e.child)S(e.child,t,n+1,r);if(e.sibling)S(e.sibling,t,n,r)}function z(e){const t=ne(e);if(t)return t;if(!e)return null;const n=e;if(n.canonical?.publicInstance)return n.canonical.publicInstance;if(n.canonical&&typeof n.canonical.measure==="function"){return n.canonical}if(typeof n.measure==="function"||typeof n.measureInWindow==="function"){return n}return null}function R(e){const t=te(e);if(t!==null)return t;if(e==null)return null;const n=e;if(typeof n.__nativeTag==="number")return n.__nativeTag;if(typeof n._nativeTag==="number")return n._nativeTag;if(typeof n.nativeTag==="number")return n.nativeTag;if(n.canonical){if(typeof n.canonical.__nativeTag==="number")return n.canonical.__nativeTag;if(typeof n.canonical._nativeTag==="number")return n.canonical._nativeTag;if(typeof n.canonical.nativeTag==="number")return n.canonical.nativeTag}return null}function fe(e){const t=z(e.stateNode);if(t){return{instance:t,nativeTag:R(e.stateNode),stateNode:e.stateNode}}const n=[];if(e.child)n.push(e.child);let r=0;while(n.length>0&&r<4e3){r++;const l=n.pop();if(!l)continue;const s=z(l.stateNode);if(s)return{instance:s,nativeTag:R(l.stateNode),stateNode:l.stateNode};if(l.child)n.push(l.child);if(l.sibling)n.push(l.sibling)}return null}function de(e){let t=e;let n=0;while(t&&n++<200){const r=t.stateNode;if(r&&typeof r.scrollTo==="function"){const l=typeof r.getNativeScrollRef==="function"&&r.getNativeScrollRef()||null;const s=typeof r.getInnerViewNode==="function"&&r.getInnerViewNode()||typeof r.getScrollableNode==="function"&&r.getScrollableNode()||r;return{instance:r,nativeRef:l,innerNode:s}}t=t.return}return null}async function he(e,t,n,r){try{if(oe(n)){n.scrollIntoView({block:"nearest",inline:"nearest",behavior:"instant"});await new Promise(a=>setTimeout(a,16));return true}const l=de(e);if(!l)return false;const s=t;let u=null;const o=a=>new Promise(i=>{let f=false;const h=y=>{if(f)return;f=true;i(y)};const g=setTimeout(()=>h(null),600);try{a(y=>{clearTimeout(g);h(y)})}catch{clearTimeout(g);h(null)}});if(l.nativeRef&&typeof s.measureLayout==="function"){u=await o(a=>s.measureLayout(l.nativeRef,(i,f)=>a(f),()=>a(null)))}if(u==null){const a=E(t);const i=typeof l.innerNode==="number"?l.innerNode:E(l.innerNode);if(a!=null&&i!=null){u=await o(f=>re.measureLayout(a,i,()=>f(null),(h,g)=>f(g)))}}if(u==null)return false;l.instance.scrollTo({y:Math.max(0,u-r),animated:false});await new Promise(a=>setTimeout(a,120));return true}catch{return false}}function U(e){const t=e?.type;if(!t)return null;if(typeof t==="string")return t;return t.displayName||t.name||null}function ge(e){let t=e?._debugOwner||e?.return;let n=0;while(t&&n<30){n++;const r=U(t);if(r&&!F.has(r)&&typeof t.type!=="string"){return r}t=t.return}return null}function b(e){return ee(e?.stateNode,e?.memoizedProps??e?.pendingProps??null)}function j(e,t=0){if(typeof e==="string")return e;if(typeof e==="number"&&Number.isFinite(e))return String(e);if(Array.isArray(e)&&t<4){const n=[];for(const r of e){const l=j(r,t+1);if(l)n.push(l)}return n.length>0?n.join(""):void 0}return void 0}function me(e){return e.replace(/\s+/g," ").trim()}function G(e){if(!e)return void 0;const t=n=>{const r=j(n);if(r===void 0)return void 0;const l=me(r);return l?l.slice(0,80):void 0};return t(e.children)??t(e.title)??t(e.label)??t(e.placeholder)??void 0}function pe(e,t){const n=[];const r=(s,u)=>{if(!s||u>t||n.length>=4)return;if(s!==e){const a=b(s);if(a&&(typeof a.onPress==="function"||typeof a.onLongPress==="function"))return}const o=G(b(s));if(o&&!n.includes(o))n.push(o);if(s.child)r(s.child,u+1);if(s!==e&&s.sibling)r(s.sibling,u)};if(e.child)r(e.child,1);const l=n.join(" ").trim();return l?l.slice(0,80):void 0}function ye(e){let t=e;let n=0;let r=null;let l=null;while(t&&n<25&&!(r&&l)){const s=b(t);if(s){if(!r&&typeof s.onPress==="function")r=s.onPress;if(!l&&typeof s.onLongPress==="function")l=s.onLongPress}t=t.return;n++}if(r)return{handler:r,kind:"onPress",longPress:l??void 0};return l?{handler:l,kind:"onLongPress"}:null}function M(e){return new Promise(t=>{let n=false;const r=l=>{if(n)return;n=true;t(l)};setTimeout(()=>r(null),1e3);try{if(typeof e.measureInWindow==="function"){e.measureInWindow((l,s,u,o)=>{if(u>0&&o>0)r({x:l,y:s,width:u,height:o});else r(null)})}else if(typeof e.measure==="function"){e.measure((l,s,u,o,a,i)=>{if(u>0&&o>0&&a!=null&&i!=null){r({x:a,y:i,width:u,height:o})}else{r(null)}})}else{r(null)}}catch{r(null)}})}const we=new Set(["RCTActivityIndicatorView","AndroidProgressBar"]);function A(){const e=B();if(e.length===0)return[];const t=[];const n=new Map;const r=new Map;const l=ae();for(const s of e){S(s.current,u=>{const o=b(u);if(!o)return;const a=typeof o.onPress==="function"||typeof o.onLongPress==="function";const i=typeof o.onValueChange==="function";const f=typeof o.onChangeText==="function";const h=o.testID||void 0;const g=o.accessibilityLabel||void 0;const y=o.accessibilityRole||o.role||void 0;let p=G(o);const P=y==="button"||y==="link"||y==="menuitem"||y==="tab"||y==="switch"||y==="checkbox";const _=typeof u.type==="string"&&we.has(u.type)&&o.animating!==false;if(!a&&!i&&!f&&!h&&!g&&!p&&!P&&!_){return}let v=null;let c=null;let d=null;if(i){const O=o.value;const Z=typeof O==="number"||typeof o.minimumValue==="number";c={handler:o.onValueChange,kind:Z?"slider":"toggle",current:O,min:typeof o.minimumValue==="number"?o.minimumValue:void 0,max:typeof o.maximumValue==="number"?o.maximumValue:void 0}}else if(f){d={handler:o.onChangeText,current:typeof o.value==="string"?o.value:void 0}}else{v=ye(u)}const w=U(u);let I=w??"Unknown";if(!w||F.has(w)||typeof u.type==="string"){I=ge(u)??w??"Unknown"}const T=fe(u);if(!T&&!h)return;if(!p&&!g&&a){p=pe(u,6)}const x=T?.instance??null;const C=T?.nativeTag??null;const m=(x?n.get(x):void 0)??(C!=null?r.get(C):void 0);if(m){if(!m.press&&v)m.press=v;else if(m.press&&v?.longPress&&!m.press.longPress){m.press.longPress=v.longPress}if(!m.valueControl&&c)m.valueControl=c;if(!m.textControl&&d)m.textControl=d;if(!m.testID&&h)m.testID=h;if(!m.label&&g)m.label=g;if(!m.text&&p)m.text=p;if(x&&!n.has(x))n.set(x,m);if(C!=null&&!r.has(C))r.set(C,m);return}const N={nativeTag:C,name:I,role:y??(_?"progressbar":void 0),testID:h,label:g,text:p,instance:x,stateNode:T?.stateNode??null,fiber:u,press:v,valueControl:c,textControl:d,rect:null,buoy:l(u)};t.push(N);if(x)n.set(x,N);if(C!=null)r.set(C,N)})}return t}function ve(e,t){if(t.nativeTag!=null)return e.nativeTag===t.nativeTag;if(t.testID&&e.testID)return e.testID.toLowerCase()===t.testID.toLowerCase();return false}function q(e){const t=A();if(e.includeBuoy)return t;return t.filter(n=>!n.buoy||ve(n,e))}async function K(e){await Promise.all(e.map(async t=>{if(t.instance)t.rect=await M(t.instance)}))}export async function describeScreen(e={}){const t=A();const n=e.includeBuoy?t:t.filter(a=>!a.buoy);const r=t.length-n.length;await K(n);const l=k.get("window");const s=Math.round(l.width)||1;const u=Math.round(l.height)||1;const o=[];for(const a of n){const i=a.rect;if(!i&&!a.testID)continue;let f;let h;if(i){const p=i.x+i.width/2;const P=i.y+i.height/2;if(P<-u*.1||P>u*1.1||p<-s*.1||p>s*1.1){continue}f={x:D(p/s),y:D(P/u)};h={x:D(i.x/s),y:D(i.y/u),width:D(i.width/s),height:D(i.height/u)}}const g=a.valueControl?a.valueControl.kind:a.textControl?"text":void 0;const y=a.valueControl?a.valueControl.current:a.textControl?.current;o.push({nativeTag:a.nativeTag,name:a.name,role:a.role,testID:a.testID,label:a.label,text:a.text,interactive:!!(a.press||a.valueControl||a.textControl),longPressable:a.press&&(a.press.kind==="onLongPress"||a.press.longPress)?true:void 0,control:g,value:y,tap:f,frame:h})}o.sort((a,i)=>{const f=a.tap?.y??2;const h=i.tap?.y??2;if(Math.abs(f-h)>.02)return f-h;return(a.tap?.x??2)-(i.tap?.x??2)});return{screen:{width:s,height:u},count:o.length,elements:o,...r>0?{hiddenBuoy:r}:{}}}function D(e){return Math.round(e*100)/100}function X(e){return{nativeTag:e.nativeTag,testID:e.testID,label:e.label,text:e.text,name:e.name}}function J(e,t){return H(X(e),t)}function Y(e,t,n){if(!e||!t||!n)return void 0;return{x:(e.x+e.width/2)/t,y:(e.y+e.height/2)/n}}function Q(e){const t=e?e.x+e.width/2:0;const n=e?e.y+e.height/2:0;const r={locationX:e?e.width/2:0,locationY:e?e.height/2:0,pageX:t,pageY:n,timestamp:0,target:null,identifier:1,touches:[],changedTouches:[]};return{nativeEvent:r,persist:()=>{},preventDefault:()=>{},stopPropagation:()=>{},isDefaultPrevented:()=>false,isPropagationStopped:()=>false,target:null,currentTarget:null,type:"press",timeStamp:0}}export async function tapElement(e){const t=q(e);if(t.length===0){return{tapped:false,reason:"No React fiber roots / elements found on screen."}}const n=t.map(c=>({c,score:J(c,e)})).filter(c=>c.score>0).sort((c,d)=>d.score-c.score);const r=c=>!!(c.press||c.valueControl||c.textControl);if(n.length===0){return{tapped:false,reason:"No element matched. Call describe_screen to see available elements (match by testID, text, or name).",candidates:t.filter(r).slice(0,12).map(c=>({nativeTag:c.nativeTag,name:c.name,testID:c.testID,text:c.text}))}}const l=k.get("window");const s=l.width||1;const u=l.height||1;const o=n.slice(0,15);await Promise.all(o.map(async c=>{if(c.c.instance&&!c.c.rect)c.c.rect=await M(c.c.instance)}));for(const c of o){c.score=H({...X(c.c),tap:Y(c.c.rect,s,u)},e)}o.sort((c,d)=>d.score-c.score);const a=o.find(c=>r(c.c))??n.find(c=>r(c.c))??o[0];const i=a.c;const f=(c,d)=>c.press&&c.press.handler===d.press?.handler||c.valueControl&&c.valueControl.handler===d.valueControl?.handler||c.textControl&&c.textControl.handler===d.textControl?.handler;const h=o.filter(c=>c!==a&&r(c.c)&&c.score===a.score).filter(c=>!f(c.c,i)).filter((c,d,w)=>w.findIndex(I=>f(I.c,c.c))===d).slice(0,3).map(c=>({nativeTag:c.c.nativeTag,name:c.c.name,testID:c.c.testID,text:c.c.text,tap:Y(c.c.rect,s,u)}));if(!r(i)){return{tapped:false,reason:`Matched "${i.name}"${i.text?` ("${i.text}")`:""} but it has no onPress / onValueChange / onChangeText handler (e.g. a react-native-gesture-handler GestureDetector). This element can't be driven in JS.`,matched:{nativeTag:i.nativeTag,name:i.name,testID:i.testID,label:i.label,text:i.text,via:"onPress"}}}if(i.instance&&!i.rect)i.rect=await M(i.instance);let g=false;const y=i.rect==null||!V(i.rect,s,u);if(y&&e.scrollIntoView!==false&&i.instance){g=await he(i.fiber,i.instance,i.stateNode,80);if(g)i.rect=await M(i.instance)}const p={nativeTag:i.nativeTag,name:i.name,testID:i.testID,label:i.label,text:i.text};se();const P=L;const _=Date.now();const v=async c=>{await new Promise(d=>setTimeout(d,le));return{tapped:true,scrolled:g,matched:c,effect:{commits:L-P,observedMs:Date.now()-_},...h.length?{alsoMatched:h}:{}}};try{if(i.press){if(e.longPress){const d=i.press.kind==="onLongPress"?i.press.handler:i.press.longPress;if(!d){return{tapped:false,scrolled:g,reason:`Matched "${i.name}"${i.text?` ("${i.text}")`:""} but it has no onLongPress handler \u2014 call tap_element without longPress to tap it normally.`,matched:{...p,via:"onPress"}}}d(Q(i.rect));return await v({...p,via:"onLongPress"})}i.press.handler(Q(i.rect));return await v({...p,via:i.press.kind})}if(i.valueControl){const d=i.valueControl;let w;if(d.kind==="toggle"){w=typeof e.value==="boolean"?e.value:!d.current}else{const I=d.min??0;const T=d.max??1;if(typeof e.value==="number"){w=Math.min(T,Math.max(I,e.value))}else{w=d.current===T?I:T}}d.handler(w);return await v({...p,via:"onValueChange",value:w})}const c=e.text??(typeof e.value==="string"?e.value:"");i.textControl.handler(c);return await v({...p,via:"onChangeText",value:c})}catch(c){return{tapped:false,scrolled:g,reason:`handler threw: ${c instanceof Error?c.message:String(c)}`,matched:{...p,via:i.press?i.press.kind:i.valueControl?"onValueChange":"onChangeText"}}}}const Te=5e3;const xe=3e4;const Ce=50;const be=150;export async function waitFor(e){const t=e.nativeTag!=null||!!String(e.testID??"").trim()||!!String(e.query??"").trim();if(!t){return{ok:false,waitedMs:0,polls:0,reason:"Nothing to wait for \u2014 pass nativeTag, testID or query."}}const n=Math.min(Math.max(e.timeoutMs??Te,0),xe);const r=Math.max(e.pollMs??be,Ce);const l=e.gone===true;const s=Date.now();let u=0;for(;;){u+=1;const{hit:o,closest:a}=await Pe(e);if(l?!o:!!o){return{ok:true,waitedMs:Date.now()-s,polls:u,matched:o??void 0}}if(Date.now()-s>=n){const i=a?.length?a:void 0;const f=l?`Still on screen after ${n}ms.`:i?`Not on screen after ${n}ms \u2014 ${i.length===1?"one element":`${i.length} elements`} matched but ${i.some(h=>h.onScreen)?"not well enough":"none is visible"}. See \`closest\`; do not retry with different words, call describeScreen.`:`Not on screen after ${n}ms, and nothing matched those words at all. Call describeScreen to see what is there.`;return{ok:false,waitedMs:Date.now()-s,polls:u,reason:f,matched:o??void 0,closest:i}}await new Promise(i=>setTimeout(i,r))}}async function Pe(e){const t=q(e).map(o=>({c:o,score:J(o,e)})).filter(o=>o.score>0).sort((o,a)=>a.score-o.score).slice(0,8);if(t.length===0)return{hit:null,closest:void 0};await K(t.map(o=>o.c));const n=k.get("window");const r=Math.round(n.width)||1;const l=Math.round(n.height)||1;let s=null;for(const{c:o}of t){if(!V(o.rect,r,l))continue;s={nativeTag:o.nativeTag,name:o.name,testID:o.testID,label:o.label,text:o.text,interactive:!!(o.press||o.valueControl||o.textControl)};break}const u=t.slice(0,3).map(({c:o})=>({name:o.name,testID:o.testID,text:o.text,onScreen:V(o.rect,r,l)}));return{hit:s,closest:u}}function Ie(e){let t=e;let n=0;while(t&&n<400){if(b(t)?.__buoyDevToolsRoot)return true;t=t.return;n++}return false}function De(e,t){const n=b(e.fiber);if(typeof n?.onPress==="function"||typeof n?.onValueChange==="function"||typeof n?.onChangeText==="function"){return e}let r=null;let l=e.fiber?.return;let s=0;while(l&&s<25){if(typeof b(l)?.onPress==="function"){r=l;break}l=l.return;s++}if(!r)return e;let u=r;for(let o=0;o<6&&u;o++){const a=t.get(u);if(a)return a;u=u.return}return e}export function resolveTouchTarget(e){const t=A();if(t.length===0)return null;const n=new Map;for(const o of t)n.set(o.fiber,o);const r=t.find(o=>o.nativeTag===e);let l=null;if(!r){for(const o of B()){S(o.current,a=>{if(l)return;if(a.stateNode&&R(a.stateNode)===e){l=a}});if(l)break}}let s=r??null;if(!s&&l){let o=l;let a=0;let i=null;while(o&&a<25){const f=n.get(o);if(f){if(f.press||f.valueControl||f.textControl){s=f;break}if(!i)i=f}o=o.return;a++}if(!s)s=i}if(!s)return null;s=De(s,n);if(!s.press&&!s.valueControl&&!s.textControl)return null;if(Ie(s.fiber))return null;const u=s.valueControl?s.valueControl.kind:s.textControl?"text":"press";return{nativeTag:s.nativeTag,name:s.name,role:s.role,testID:s.testID,label:s.label,text:s.text,control:u,value:s.valueControl?s.valueControl.current:s.textControl?s.textControl.current:void 0}}
1
+ "use strict";import{getDOMControlProps as oe,getDOMNodeHandle as re,getDOMPublicInstance as ie,isDOMElement as se}from"@buoy-gg/shared-ui/utils";import{Dimensions as L,UIManager as le,findNodeHandle as $}from"react-native";import{isRectCenterOnScreen as V,scoreTapMatch as W}from"./tapMatch";const Le=5;const ae=22;const z=new Set(["View","RCTView","ScrollView","RCTScrollView","RCTScrollContentView","SafeAreaView","RCTSafeAreaView","AndroidHorizontalScrollContentView","KeyboardAvoidingView","Pressable","PlatformPressable","TouchableOpacity","TouchableHighlight","TouchableWithoutFeedback","TouchableNativeFeedback","AnimatedComponent","Wrapper"]);function B(){if(typeof global==="undefined")return null;return global.__REACT_DEVTOOLS_GLOBAL_HOOK__??null}let R=0;let U=false;function ce(){if(U)return;const e=B();if(!e)return;const t=e.onCommitFiberRoot;e.onCommitFiberRoot=function(...n){R++;return typeof t==="function"?t.apply(e,n):void 0};U=true}const ue=250;function j(){const e=new Map;return t=>{const n=[];let i=t;let l=0;let s=false;while(i&&l<400){const a=e.get(i);if(a!==void 0){s=a;break}if(I(i)?.__buoyDevToolsRoot){s=true;e.set(i,true);break}n.push(i);i=i.return;l++}for(const a of n)e.set(a,s);return s}}function A(){const e=B();if(!e)return[];const t=[];try{if(e.renderers&&typeof e.renderers.forEach==="function"){e.renderers.forEach((n,i)=>{try{const l=e.getFiberRoots?.(i);if(l)l.forEach(s=>t.push(s))}catch{}})}if(t.length===0&&e.getFiberRoots){const n=e.getFiberRoots(1);if(n)n.forEach(i=>t.push(i))}}catch{}return t}function fe(e){return e.tag===ae&&e.memoizedState!==null}function de(e){const t=e.memoizedProps?.activityState;if(t===0||t===1)return true;if(e.memoizedProps?.isPageFocused===false)return true;return false}function S(e,t,n=0,i=new Set){if(!e||i.has(e)||n>500)return;i.add(e);if(fe(e)||de(e)){if(e.sibling)S(e.sibling,t,n,i);return}t(e);if(e.child)S(e.child,t,n+1,i);if(e.sibling)S(e.sibling,t,n,i)}function G(e){const t=ie(e);if(t)return t;if(!e)return null;const n=e;if(n.canonical?.publicInstance)return n.canonical.publicInstance;if(n.canonical&&typeof n.canonical.measure==="function"){return n.canonical}if(typeof n.measure==="function"||typeof n.measureInWindow==="function"){return n}return null}function O(e){const t=re(e);if(t!==null)return t;if(e==null)return null;const n=e;if(typeof n.__nativeTag==="number")return n.__nativeTag;if(typeof n._nativeTag==="number")return n._nativeTag;if(typeof n.nativeTag==="number")return n.nativeTag;if(n.canonical){if(typeof n.canonical.__nativeTag==="number")return n.canonical.__nativeTag;if(typeof n.canonical._nativeTag==="number")return n.canonical._nativeTag;if(typeof n.canonical.nativeTag==="number")return n.canonical.nativeTag}return null}function q(e){const t=G(e.stateNode);if(t){return{instance:t,nativeTag:O(e.stateNode),stateNode:e.stateNode}}const n=[];if(e.child)n.push(e.child);let i=0;while(n.length>0&&i<4e3){i++;const l=n.pop();if(!l)continue;const s=G(l.stateNode);if(s)return{instance:s,nativeTag:O(l.stateNode),stateNode:l.stateNode};if(l.child)n.push(l.child);if(l.sibling)n.push(l.sibling)}return null}function he(e){let t=e;let n=0;while(t&&n++<200){const i=t.stateNode;if(i&&typeof i.scrollTo==="function"){const l=typeof i.getNativeScrollRef==="function"&&i.getNativeScrollRef()||null;const s=typeof i.getInnerViewNode==="function"&&i.getInnerViewNode()||typeof i.getScrollableNode==="function"&&i.getScrollableNode()||i;return{instance:i,nativeRef:l,innerNode:s}}t=t.return}return null}async function ge(e,t,n,i){try{if(se(n)){n.scrollIntoView({block:"nearest",inline:"nearest",behavior:"instant"});await new Promise(u=>setTimeout(u,16));return true}const l=he(e);if(!l)return false;const s=t;let a=null;const r=u=>new Promise(o=>{let f=false;const d=m=>{if(f)return;f=true;o(m)};const h=setTimeout(()=>d(null),600);try{u(m=>{clearTimeout(h);d(m)})}catch{clearTimeout(h);d(null)}});if(l.nativeRef&&typeof s.measureLayout==="function"){a=await r(u=>s.measureLayout(l.nativeRef,(o,f)=>u(f),()=>u(null)))}if(a==null){const u=$(t);const o=typeof l.innerNode==="number"?l.innerNode:$(l.innerNode);if(u!=null&&o!=null){a=await r(f=>le.measureLayout(u,o,()=>f(null),(d,h)=>f(h)))}}if(a==null)return false;l.instance.scrollTo({y:Math.max(0,a-i),animated:false});await new Promise(u=>setTimeout(u,120));return true}catch{return false}}function N(e){const t=e?.type;if(!t)return null;if(typeof t==="string")return t;return t.displayName||t.name||null}function E(e){let t=e?._debugOwner||e?.return;let n=0;while(t&&n<30){n++;const i=N(t);if(i&&!z.has(i)&&typeof t.type!=="string"){return i}t=t.return}return null}function I(e){return oe(e?.stateNode,e?.memoizedProps??e?.pendingProps??null)}function K(e,t=0){if(typeof e==="string")return e;if(typeof e==="number"&&Number.isFinite(e))return String(e);if(Array.isArray(e)&&t<4){const n=[];for(const i of e){const l=K(i,t+1);if(l)n.push(l)}return n.length>0?n.join(""):void 0}return void 0}function me(e){return e.replace(/\s+/g," ").trim()}function X(e){if(!e)return void 0;const t=n=>{const i=K(n);if(i===void 0)return void 0;const l=me(i);return l?l.slice(0,80):void 0};return t(e.children)??t(e.title)??t(e.label)??t(e.placeholder)??void 0}function pe(e,t){const n=[];const i=(s,a)=>{if(!s||a>t||n.length>=4)return;if(s!==e){const u=I(s);if(u&&(typeof u.onPress==="function"||typeof u.onLongPress==="function"))return}const r=X(I(s));if(r&&!n.includes(r))n.push(r);if(s.child)i(s.child,a+1);if(s!==e&&s.sibling)i(s.sibling,a)};if(e.child)i(e.child,1);const l=n.join(" ").trim();return l?l.slice(0,80):void 0}function ye(e){let t=e;let n=0;let i=null;let l=null;while(t&&n<25&&!(i&&l)){const s=I(t);if(s){if(!i&&typeof s.onPress==="function")i=s.onPress;if(!l&&typeof s.onLongPress==="function")l=s.onLongPress}t=t.return;n++}if(i)return{handler:i,kind:"onPress",longPress:l??void 0};return l?{handler:l,kind:"onLongPress"}:null}function M(e){return new Promise(t=>{let n=false;const i=l=>{if(n)return;n=true;t(l)};setTimeout(()=>i(null),1e3);try{if(typeof e.measureInWindow==="function"){e.measureInWindow((l,s,a,r)=>{if(a>0&&r>0)i({x:l,y:s,width:a,height:r});else i(null)})}else if(typeof e.measure==="function"){e.measure((l,s,a,r,u,o)=>{if(a>0&&r>0&&u!=null&&o!=null){i({x:u,y:o,width:a,height:r})}else{i(null)}})}else{i(null)}}catch{i(null)}})}const we=new Set(["RCTActivityIndicatorView","AndroidProgressBar"]);function F(){const e=A();if(e.length===0)return[];const t=[];const n=new Map;const i=new Map;const l=j();for(const s of e){S(s.current,a=>{const r=I(a);if(!r)return;const u=typeof r.onPress==="function"||typeof r.onLongPress==="function";const o=typeof r.onValueChange==="function";const f=typeof r.onChangeText==="function";const d=r.testID||void 0;const h=r.accessibilityLabel||void 0;const m=r.accessibilityRole||r.role||void 0;let p=X(r);const w=m==="button"||m==="link"||m==="menuitem"||m==="tab"||m==="switch"||m==="checkbox";const T=typeof a.type==="string"&&we.has(a.type)&&r.animating!==false;if(!u&&!o&&!f&&!d&&!h&&!p&&!w&&!T){return}let x=null;let c=null;let g=null;if(o){const H=r.value;const ne=typeof H==="number"||typeof r.minimumValue==="number";c={handler:r.onValueChange,kind:ne?"slider":"toggle",current:H,min:typeof r.minimumValue==="number"?r.minimumValue:void 0,max:typeof r.maximumValue==="number"?r.maximumValue:void 0}}else if(f){g={handler:r.onChangeText,current:typeof r.value==="string"?r.value:void 0}}else{x=ye(a)}const v=N(a);let D=v??"Unknown";if(!v||z.has(v)||typeof a.type==="string"){D=E(a)??v??"Unknown"}const C=q(a);if(!C&&!d)return;if(!p&&!h&&u){p=pe(a,6)}const b=C?.instance??null;const P=C?.nativeTag??null;const y=(b?n.get(b):void 0)??(P!=null?i.get(P):void 0);if(y){if(!y.press&&x)y.press=x;else if(y.press&&x?.longPress&&!y.press.longPress){y.press.longPress=x.longPress}if(!y.valueControl&&c)y.valueControl=c;if(!y.textControl&&g)y.textControl=g;if(!y.testID&&d)y.testID=d;if(!y.label&&h)y.label=h;if(!y.text&&p)y.text=p;if(b&&!n.has(b))n.set(b,y);if(P!=null&&!i.has(P))i.set(P,y);return}const k={nativeTag:P,name:D,role:m??(T?"progressbar":void 0),testID:d,label:h,text:p,instance:b,stateNode:C?.stateNode??null,fiber:a,press:x,valueControl:c,textControl:g,rect:null,buoy:l(a)};t.push(k);if(b)n.set(b,k);if(P!=null)i.set(P,k)})}return t}function ve(e,t){if(t.nativeTag!=null)return e.nativeTag===t.nativeTag;if(t.testID&&e.testID)return e.testID.toLowerCase()===t.testID.toLowerCase();return false}function J(e){const t=F();if(e.includeBuoy)return t;return t.filter(n=>!n.buoy||ve(n,e))}async function Y(e){await Promise.all(e.map(async t=>{if(t.instance)t.rect=await M(t.instance)}))}const Te=/^(FlatList|SectionList|VirtualizedSectionList|VirtualizedList|Animated\(.*\)|AnimatedComponent.*)$/;async function xe(e,t){const n=[];const i=j();for(const s of A()){S(s.current,a=>{if(N(a)!=="VirtualizedList")return;const r=a.memoizedProps;if(!r||typeof r.getItemCount!=="function")return;if(!e&&i(a))return;let u;try{u=Number(r.getItemCount(r.data))}catch{return}if(!Number.isFinite(u)||u<=0)return;const o=[];const f=(m,p)=>{for(let w=m?.child;w;w=w.sibling){const T=N(w);if(T==="VirtualizedList")continue;if(T==="CellRenderer"){o.push(w);continue}if(p<40)f(w,p+1)}};f(a,0);let d=a;let h=E(d);for(let m=0;m<6&&h&&Te.test(h);m++){d=d._debugOwner||d.return;h=E(d)}n.push({owner:h,items:u,cells:o})})}const l=[];for(const s of n){const a=await Promise.all(s.cells.map(async u=>{const o=q(u);return o?M(o.instance):null}));const r=a.filter(t).length;if(r>0&&s.items>r)l.push({owner:s.owner,items:s.items,listed:r})}return l}export async function describeScreen(e={}){const t=F();const n=e.includeBuoy?t:t.filter(o=>!o.buoy);const i=t.length-n.length;await Y(n);const l=L.get("window");const s=Math.round(l.width)||1;const a=Math.round(l.height)||1;const r=[];for(const o of n){const f=o.rect;if(!f&&!o.testID)continue;let d;let h;if(f){const w=f.x+f.width/2;const T=f.y+f.height/2;if(T<-a*.1||T>a*1.1||w<-s*.1||w>s*1.1){continue}d={x:_(w/s),y:_(T/a)};h={x:_(f.x/s),y:_(f.y/a),width:_(f.width/s),height:_(f.height/a)}}const m=o.valueControl?o.valueControl.kind:o.textControl?"text":void 0;const p=o.valueControl?o.valueControl.current:o.textControl?.current;r.push({nativeTag:o.nativeTag,name:o.name,role:o.role,testID:o.testID,label:o.label,text:o.text,interactive:!!(o.press||o.valueControl||o.textControl),longPressable:o.press&&(o.press.kind==="onLongPress"||o.press.longPress)?true:void 0,control:m,value:p,tap:d,frame:h})}r.sort((o,f)=>{const d=o.tap?.y??2;const h=f.tap?.y??2;if(Math.abs(d-h)>.02)return d-h;return(o.tap?.x??2)-(f.tap?.x??2)});const u=await xe(!!e.includeBuoy,o=>{if(!o)return false;const f=o.x+o.width/2;return o.y<=a*1.1&&o.y+o.height>=-a*.1&&f>=-s*.1&&f<=s*1.1});return{screen:{width:s,height:a},count:r.length,elements:r,...i>0?{hiddenBuoy:i}:{},...u.length?{lists:u}:{}}}function _(e){return Math.round(e*100)/100}function Q(e){return{nativeTag:e.nativeTag,testID:e.testID,label:e.label,text:e.text,name:e.name}}function Z(e,t){return W(Q(e),t)}function ee(e,t,n){if(!e||!t||!n)return void 0;return{x:(e.x+e.width/2)/t,y:(e.y+e.height/2)/n}}function te(e){const t=e?e.x+e.width/2:0;const n=e?e.y+e.height/2:0;const i={locationX:e?e.width/2:0,locationY:e?e.height/2:0,pageX:t,pageY:n,timestamp:0,target:null,identifier:1,touches:[],changedTouches:[]};return{nativeEvent:i,persist:()=>{},preventDefault:()=>{},stopPropagation:()=>{},isDefaultPrevented:()=>false,isPropagationStopped:()=>false,target:null,currentTarget:null,type:"press",timeStamp:0}}export async function tapElement(e){const t=J(e);if(t.length===0){return{tapped:false,reason:"No React fiber roots / elements found on screen."}}const n=t.map(c=>({c,score:Z(c,e)})).filter(c=>c.score>0).sort((c,g)=>g.score-c.score);const i=c=>!!(c.press||c.valueControl||c.textControl);if(n.length===0){return{tapped:false,reason:"No element matched. Call describe_screen to see available elements (match by testID, text, or name).",candidates:t.filter(i).slice(0,12).map(c=>({nativeTag:c.nativeTag,name:c.name,testID:c.testID,text:c.text}))}}const l=L.get("window");const s=l.width||1;const a=l.height||1;const r=n.slice(0,15);await Promise.all(r.map(async c=>{if(c.c.instance&&!c.c.rect)c.c.rect=await M(c.c.instance)}));for(const c of r){c.score=W({...Q(c.c),tap:ee(c.c.rect,s,a)},e)}r.sort((c,g)=>g.score-c.score);const u=r.find(c=>i(c.c))??n.find(c=>i(c.c))??r[0];const o=u.c;const f=(c,g)=>c.press&&c.press.handler===g.press?.handler||c.valueControl&&c.valueControl.handler===g.valueControl?.handler||c.textControl&&c.textControl.handler===g.textControl?.handler;const d=r.filter(c=>c!==u&&i(c.c)&&c.score===u.score).filter(c=>!f(c.c,o)).filter((c,g,v)=>v.findIndex(D=>f(D.c,c.c))===g).slice(0,3).map(c=>({nativeTag:c.c.nativeTag,name:c.c.name,testID:c.c.testID,text:c.c.text,tap:ee(c.c.rect,s,a)}));if(!i(o)){return{tapped:false,reason:`Matched "${o.name}"${o.text?` ("${o.text}")`:""} but it has no onPress / onValueChange / onChangeText handler (e.g. a react-native-gesture-handler GestureDetector). This element can't be driven in JS.`,matched:{nativeTag:o.nativeTag,name:o.name,testID:o.testID,label:o.label,text:o.text,via:"onPress"}}}if(o.instance&&!o.rect)o.rect=await M(o.instance);let h=false;const m=o.rect==null||!V(o.rect,s,a);if(m&&e.scrollIntoView!==false&&o.instance){h=await ge(o.fiber,o.instance,o.stateNode,80);if(h)o.rect=await M(o.instance)}const p={nativeTag:o.nativeTag,name:o.name,testID:o.testID,label:o.label,text:o.text};ce();const w=R;const T=Date.now();const x=async c=>{await new Promise(g=>setTimeout(g,ue));return{tapped:true,scrolled:h,matched:c,effect:{commits:R-w,observedMs:Date.now()-T},...d.length?{alsoMatched:d}:{}}};try{if(o.press){if(e.longPress){const g=o.press.kind==="onLongPress"?o.press.handler:o.press.longPress;if(!g){return{tapped:false,scrolled:h,reason:`Matched "${o.name}"${o.text?` ("${o.text}")`:""} but it has no onLongPress handler \u2014 call tap_element without longPress to tap it normally.`,matched:{...p,via:"onPress"}}}g(te(o.rect));return await x({...p,via:"onLongPress"})}o.press.handler(te(o.rect));return await x({...p,via:o.press.kind})}if(o.valueControl){const g=o.valueControl;let v;if(g.kind==="toggle"){v=typeof e.value==="boolean"?e.value:!g.current}else{const D=g.min??0;const C=g.max??1;if(typeof e.value==="number"){v=Math.min(C,Math.max(D,e.value))}else{v=g.current===C?D:C}}g.handler(v);return await x({...p,via:"onValueChange",value:v})}const c=e.text??(typeof e.value==="string"?e.value:"");o.textControl.handler(c);return await x({...p,via:"onChangeText",value:c})}catch(c){return{tapped:false,scrolled:h,reason:`handler threw: ${c instanceof Error?c.message:String(c)}`,matched:{...p,via:o.press?o.press.kind:o.valueControl?"onValueChange":"onChangeText"}}}}const Ce=5e3;const be=3e4;const Pe=50;const Ie=150;export async function waitFor(e){const t=e.nativeTag!=null||!!String(e.testID??"").trim()||!!String(e.query??"").trim();if(!t){return{ok:false,waitedMs:0,polls:0,reason:"Nothing to wait for \u2014 pass nativeTag, testID or query."}}const n=Math.min(Math.max(e.timeoutMs??Ce,0),be);const i=Math.max(e.pollMs??Ie,Pe);const l=e.gone===true;const s=Date.now();let a=0;for(;;){a+=1;const{hit:r,closest:u}=await De(e);if(l?!r:!!r){return{ok:true,waitedMs:Date.now()-s,polls:a,matched:r??void 0}}if(Date.now()-s>=n){const o=u?.length?u:void 0;const f=l?`Still on screen after ${n}ms.`:o?`Not on screen after ${n}ms \u2014 ${o.length===1?"one element":`${o.length} elements`} matched but ${o.some(d=>d.onScreen)?"not well enough":"none is visible"}. See \`closest\`; do not retry with different words, call describeScreen.`:`Not on screen after ${n}ms, and nothing matched those words at all. Call describeScreen to see what is there.`;return{ok:false,waitedMs:Date.now()-s,polls:a,reason:f,matched:r??void 0,closest:o}}await new Promise(o=>setTimeout(o,i))}}async function De(e){const t=J(e).map(r=>({c:r,score:Z(r,e)})).filter(r=>r.score>0).sort((r,u)=>u.score-r.score).slice(0,8);if(t.length===0)return{hit:null,closest:void 0};await Y(t.map(r=>r.c));const n=L.get("window");const i=Math.round(n.width)||1;const l=Math.round(n.height)||1;let s=null;for(const{c:r}of t){if(!V(r.rect,i,l))continue;s={nativeTag:r.nativeTag,name:r.name,testID:r.testID,label:r.label,text:r.text,interactive:!!(r.press||r.valueControl||r.textControl)};break}const a=t.slice(0,3).map(({c:r})=>({name:r.name,testID:r.testID,text:r.text,onScreen:V(r.rect,i,l)}));return{hit:s,closest:a}}function Se(e){let t=e;let n=0;while(t&&n<400){if(I(t)?.__buoyDevToolsRoot)return true;t=t.return;n++}return false}function _e(e,t){const n=I(e.fiber);if(typeof n?.onPress==="function"||typeof n?.onValueChange==="function"||typeof n?.onChangeText==="function"){return e}let i=null;let l=e.fiber?.return;let s=0;while(l&&s<25){if(typeof I(l)?.onPress==="function"){i=l;break}l=l.return;s++}if(!i)return e;let a=i;for(let r=0;r<6&&a;r++){const u=t.get(a);if(u)return u;a=a.return}return e}export function resolveTouchTarget(e){const t=F();if(t.length===0)return null;const n=new Map;for(const r of t)n.set(r.fiber,r);const i=t.find(r=>r.nativeTag===e);let l=null;if(!i){for(const r of A()){S(r.current,u=>{if(l)return;if(u.stateNode&&O(u.stateNode)===e){l=u}});if(l)break}}let s=i??null;if(!s&&l){let r=l;let u=0;let o=null;while(r&&u<25){const f=n.get(r);if(f){if(f.press||f.valueControl||f.textControl){s=f;break}if(!o)o=f}r=r.return;u++}if(!s)s=o}if(!s)return null;s=_e(s,n);if(!s.press&&!s.valueControl&&!s.textControl)return null;if(Se(s.fiber))return null;const a=s.valueControl?s.valueControl.kind:s.textControl?"text":"press";return{nativeTag:s.nativeTag,name:s.name,role:s.role,testID:s.testID,label:s.label,text:s.text,control:a,value:s.valueControl?s.valueControl.current:s.textControl?s.textControl.current:void 0}}
@@ -47,6 +47,20 @@ export interface DescribeScreenResult {
47
47
  elements: ScreenElement[];
48
48
  /** Elements under Buoy's own overlay that were left out (pass includeBuoy to see them). */
49
49
  hiddenBuoy?: number;
50
+ /**
51
+ * Scrolling lists (FlatList, SectionList) that hold more items than
52
+ * `elements` shows. Rows below the fold are left out, so a list of 40 can
53
+ * look like a list of 6.
54
+ */
55
+ lists?: VirtualList[];
56
+ }
57
+ export interface VirtualList {
58
+ /** The component that renders the list, e.g. "OffersScreen". */
59
+ owner: string | null;
60
+ /** Items in the list's data. */
61
+ items: number;
62
+ /** Rows that made it into `elements`; the rest are below the fold or not mounted. */
63
+ listed: number;
50
64
  }
51
65
  export interface TapElementParams {
52
66
  nativeTag?: number;