@@ -1056,6 +1056,31 @@ async function resolveKnownSystemPromptsWithQueries(label) {
10561056 ) ;
10571057 await sleep ( 500 ) ;
10581058 }
1059+
1060+ const snapshot = await queryUi ( label , {
1061+ limit : 128 ,
1062+ maxDepth : 8 ,
1063+ maxElapsedMs : httpActionBudgetMs ,
1064+ } ) ;
1065+ if ( ! looksLikeKeyboardTipQuery ( snapshot ) ) {
1066+ return ;
1067+ }
1068+
1069+ logStep ( `handling system keyboard-tip prompt after ${ label } ` ) ;
1070+ await retryHttpJson (
1071+ "POST" ,
1072+ `/api/simulators/${ simulatorUDID } /tap` ,
1073+ {
1074+ source : "native-ax" ,
1075+ maxDepth : 8 ,
1076+ waitTimeoutMs : 2_000 ,
1077+ durationMs : 80 ,
1078+ ...keyboardTipContinueTapTarget ( snapshot ) ,
1079+ } ,
1080+ `${ label } tap keyboard-tip prompt` ,
1081+ { attempts : 1 , maxElapsedMs : httpActionBudgetMs } ,
1082+ ) ;
1083+ await sleep ( 500 ) ;
10591084}
10601085
10611086function summarizeUiCheck ( options = { } ) {
@@ -1387,6 +1412,96 @@ function looksLikeKeyboardTipPrompt(snapshot) {
13871412 ) ;
13881413}
13891414
1415+ function looksLikeKeyboardTipQuery ( snapshot ) {
1416+ const text = JSON . stringify ( snapshot ?. matches ?? snapshot ?? [ ] ) ;
1417+ return / S p e e d u p y o u r t y p i n g / i. test ( text ) && / \b C o n t i n u e \b / . test ( text ) ;
1418+ }
1419+
1420+ function keyboardTipContinueTapTarget ( snapshot ) {
1421+ const nodes = compactQueryNodes ( snapshot ) ;
1422+ const rootFrame = nodes
1423+ . map ( ( node ) => node . frame )
1424+ . filter ( validFrame )
1425+ . sort ( ( a , b ) => b . width * b . height - a . width * a . height ) [ 0 ] ;
1426+ const continueButton =
1427+ nodes . find (
1428+ ( node ) =>
1429+ node . label === "Continue" &&
1430+ node . id !== "fixture.continue" &&
1431+ String ( node . role ?? "" )
1432+ . toLowerCase ( )
1433+ . includes ( "button" ) &&
1434+ validFrame ( node . frame ) ,
1435+ ) ??
1436+ nodes . find (
1437+ ( node ) =>
1438+ node . label === "Continue" &&
1439+ String ( node . role ?? "" )
1440+ . toLowerCase ( )
1441+ . includes ( "button" ) &&
1442+ validFrame ( node . frame ) ,
1443+ ) ;
1444+
1445+ if ( rootFrame && continueButton ?. frame ) {
1446+ return {
1447+ normalized : true ,
1448+ x :
1449+ ( continueButton . frame . x +
1450+ continueButton . frame . width / 2 -
1451+ rootFrame . x ) /
1452+ rootFrame . width ,
1453+ y :
1454+ ( continueButton . frame . y +
1455+ continueButton . frame . height / 2 -
1456+ rootFrame . y ) /
1457+ rootFrame . height ,
1458+ } ;
1459+ }
1460+
1461+ return {
1462+ selector : { label : "Continue" , elementType : "Button" } ,
1463+ } ;
1464+ }
1465+
1466+ function compactQueryNodes ( snapshot ) {
1467+ const nodes = [ ] ;
1468+ const visit = ( node ) => {
1469+ if ( ! node || typeof node !== "object" ) {
1470+ return ;
1471+ }
1472+ if (
1473+ "role" in node ||
1474+ "id" in node ||
1475+ "label" in node ||
1476+ "value" in node ||
1477+ "frame" in node
1478+ ) {
1479+ nodes . push ( node ) ;
1480+ }
1481+ for ( const child of Array . isArray ( node . children ) ? node . children : [ ] ) {
1482+ visit ( child ) ;
1483+ }
1484+ } ;
1485+ for ( const match of Array . isArray ( snapshot ?. matches )
1486+ ? snapshot . matches
1487+ : [ ] ) {
1488+ visit ( match ) ;
1489+ }
1490+ return nodes ;
1491+ }
1492+
1493+ function validFrame ( frame ) {
1494+ return (
1495+ frame &&
1496+ Number . isFinite ( frame . x ) &&
1497+ Number . isFinite ( frame . y ) &&
1498+ Number . isFinite ( frame . width ) &&
1499+ Number . isFinite ( frame . height ) &&
1500+ frame . width > 0 &&
1501+ frame . height > 0
1502+ ) ;
1503+ }
1504+
13901505function fixtureReady ( snapshot ) {
13911506 return (
13921507 snapshot . includes ( "SimDeck Fixture" ) &&
0 commit comments