peachsim 0.1.577 → 0.1.579

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (178) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +11 -9
  3. package/cli/app-catalog.ts +77 -0
  4. package/cli/app-fonts.ts +4 -20
  5. package/cli/app-splash.ts +2 -18
  6. package/cli/bridge-flow-runner.ts +505 -139
  7. package/cli/cloud-client.ts +1 -4
  8. package/cli/cloud-session.ts +1 -4
  9. package/cli/cloud-test-result-files.ts +3 -0
  10. package/cli/cloud-test-tunnel.ts +2 -5
  11. package/cli/command-registry.ts +2 -0
  12. package/cli/commands/agent.ts +149 -23
  13. package/cli/commands/assert.ts +1 -4
  14. package/cli/commands/box/peach-command.ts +62 -0
  15. package/cli/commands/box/plane-share.ts +0 -70
  16. package/cli/commands/box/rnx-command.ts +1 -60
  17. package/cli/commands/box/search-command.ts +14 -0
  18. package/cli/commands/box.ts +2 -2
  19. package/cli/commands/control.ts +121 -27
  20. package/cli/commands/cpu-profile.ts +23 -10
  21. package/cli/commands/daemon.ts +1 -9
  22. package/cli/commands/detox.ts +26 -18
  23. package/cli/commands/device.ts +129 -1
  24. package/cli/commands/film.ts +25 -16
  25. package/cli/commands/flow.ts +9 -2
  26. package/cli/commands/inspect/actions.ts +75 -1
  27. package/cli/commands/inspect/core.ts +81 -2
  28. package/cli/commands/inspect/describe.ts +2 -6
  29. package/cli/commands/inspect/env.ts +1 -1
  30. package/cli/commands/inspect/settling.ts +20 -3
  31. package/cli/commands/inspect/shared.ts +1 -4
  32. package/cli/commands/inspect/sleep.ts +1 -1
  33. package/cli/commands/inspect.ts +16 -49
  34. package/cli/commands/install-desktop.ts +8 -6
  35. package/cli/commands/login.ts +3 -5
  36. package/cli/commands/maestro-generate.ts +1 -4
  37. package/cli/commands/maestro.ts +38 -21
  38. package/cli/commands/no-bridge-hint.ts +0 -14
  39. package/cli/commands/permissions.ts +2 -2
  40. package/cli/commands/platform.ts +13 -6
  41. package/cli/commands/screenshot.ts +6 -1
  42. package/cli/commands/skills.ts +1 -1
  43. package/cli/commands/test.ts +6 -2
  44. package/cli/commands/three-mode.ts +162 -19
  45. package/cli/commands/upload.ts +3 -18
  46. package/cli/current-sim.ts +0 -1
  47. package/cli/desktop-companion.ts +40 -8
  48. package/cli/drivers/playwright-provisioning.ts +1 -4
  49. package/cli/flow-export.ts +1 -8
  50. package/cli/flow-session.ts +0 -4
  51. package/cli/hints.ts +1 -16
  52. package/cli/inspect-notice-state.ts +0 -4
  53. package/cli/main.ts +2 -4
  54. package/cli/outbound-endpoints.ts +1 -24
  55. package/cli/parse-args.ts +1 -2
  56. package/cli/rnx-in-process.ts +3 -1
  57. package/cli/run-rnx-core.ts +4 -2
  58. package/cli/run-rnx.ts +18 -10
  59. package/cli/send-to-box.ts +1 -4
  60. package/cli/telemetry.ts +1 -1
  61. package/cli/ws-bridge.ts +1 -5
  62. package/detox/gestures.ts +20 -6
  63. package/detox/index.ts +379 -123
  64. package/detox/navigation-lifecycle.ts +22 -13
  65. package/dist-lib/agent-daemon-client.cjs +21 -15
  66. package/dist-lib/agent-events.cjs +1 -1
  67. package/dist-lib/agent-identity.cjs +3 -17
  68. package/dist-lib/agent-sessions.cjs +5 -4
  69. package/dist-lib/attached-projects.cjs +1 -77
  70. package/dist-lib/auth/shared-session.cjs +1 -1
  71. package/dist-lib/backend-origin.cjs +11 -1
  72. package/dist-lib/beta.cjs +1 -1
  73. package/dist-lib/beta.mjs +1 -1
  74. package/dist-lib/bridge-constants.cjs +1 -1
  75. package/dist-lib/bridge-contract-input.cjs +7 -5
  76. package/dist-lib/bridge-contract-input.mjs +7 -5
  77. package/dist-lib/bridge-contract.cjs +1 -1
  78. package/dist-lib/bridge-contract.mjs +1 -1
  79. package/dist-lib/capture-contract.cjs +30 -7
  80. package/dist-lib/capture-contract.mjs +30 -7
  81. package/dist-lib/cli-constants.cjs +1 -1
  82. package/dist-lib/cloud-contract.cjs +1 -1
  83. package/dist-lib/cloud-contract.mjs +1 -1
  84. package/dist-lib/cloud-test-args.cjs +1 -1
  85. package/dist-lib/cloud-test-args.mjs +1 -1
  86. package/dist-lib/cloud.cjs +17 -11
  87. package/dist-lib/cloud.mjs +17 -11
  88. package/dist-lib/config.cjs +1 -1
  89. package/dist-lib/detox/index.cjs +321 -122
  90. package/dist-lib/dev-bundle-resolution.cjs +1 -1
  91. package/dist-lib/home-paths.cjs +1 -1
  92. package/dist-lib/host/bridge-host.cjs +427 -212
  93. package/dist-lib/host/fetch-proxy-handler.cjs +5 -28
  94. package/dist-lib/host/fetch-proxy-overrides.cjs +1 -1
  95. package/dist-lib/host/fetch-proxy-overrides.mjs +1 -1
  96. package/dist-lib/host/replacement-module-handler.cjs +1 -1
  97. package/dist-lib/host/websocket-proxy.cjs +1 -1
  98. package/dist-lib/index.cjs +315 -58
  99. package/dist-lib/jump-to-source-babel.cjs +1 -1
  100. package/dist-lib/jump-to-source-native.cjs +1 -1
  101. package/dist-lib/menu.cjs +67 -31
  102. package/dist-lib/menu.mjs +64 -28
  103. package/dist-lib/metro-fingerprint-registry.cjs +8 -4
  104. package/dist-lib/metro-fingerprint-registry.mjs +6 -4
  105. package/dist-lib/metro-production-bundle.cjs +2 -2
  106. package/dist-lib/metro-production-bundle.mjs +2 -2
  107. package/dist-lib/metro.cjs +8 -4
  108. package/dist-lib/profiles.cjs +1 -1
  109. package/dist-lib/public-brand.cjs +7 -4
  110. package/dist-lib/react-native-host-modules.cjs +1 -1
  111. package/dist-lib/react-native-host-modules.mjs +1 -1
  112. package/dist-lib/render-mode.cjs +3 -13
  113. package/dist-lib/scripts/dev-server-scanner.cjs +5 -17
  114. package/dist-lib/sdk.cjs +129 -10
  115. package/dist-lib/sdk.mjs +127 -10
  116. package/dist-lib/skills.cjs +1635 -1121
  117. package/dist-lib/swift-compile.cjs +277 -70
  118. package/dist-lib/swift-compile.mjs +275 -70
  119. package/dist-lib/swift-package.cjs +17 -5
  120. package/dist-lib/swift-package.mjs +15 -4
  121. package/dist-lib/swift-submit.cjs +32 -8
  122. package/dist-lib/swift-submit.mjs +29 -7
  123. package/dist-lib/swift.cjs +30912 -577
  124. package/dist-lib/vite-kotlin.cjs +356 -0
  125. package/dist-lib/vite-swift.cjs +1743 -0
  126. package/dist-lib/vite.cjs +208 -1039
  127. package/kotlin/README.md +18 -0
  128. package/kotlin/Sources/androidx/annotation/Keep.kt +4 -0
  129. package/kotlin/Sources/dev/onejs/one/OneNativeSourceDispatch.kt +7 -0
  130. package/kotlin/Sources/dev/onejs/one/source/OneModule.kt +4 -0
  131. package/kotlin/Sources/org/json/JSON.kt +244 -0
  132. package/kotlin/toolchain.json +19 -0
  133. package/package.json +14 -2
  134. package/scripts/dev-server-scanner.ts +0 -18
  135. package/scripts/optional-demo-registry.ts +0 -17
  136. package/skills/contrast/SKILL.md +2 -2
  137. package/skills/peach-setup/SKILL.md +16 -0
  138. package/src/agent-daemon-client.ts +36 -22
  139. package/src/agent-identity.ts +0 -15
  140. package/src/agent-prompt.ts +12 -0
  141. package/src/attached-projects.ts +0 -72
  142. package/src/backend-origin.ts +10 -0
  143. package/src/bridge-contract-input.ts +2 -5
  144. package/src/bridge-contract.ts +2 -0
  145. package/src/capture-contract.ts +53 -11
  146. package/src/cloud.ts +4 -9
  147. package/src/dev-server-open.ts +0 -99
  148. package/src/home-paths.ts +2 -3
  149. package/src/host/agent-host.ts +82 -36
  150. package/src/host/bridge-host.ts +5 -11
  151. package/src/host/fetch-proxy-handler.ts +19 -43
  152. package/src/is-record.ts +4 -0
  153. package/src/menu.ts +82 -37
  154. package/src/metro-fingerprint-registry.ts +1 -4
  155. package/src/metro-production-bundle.ts +1 -1
  156. package/src/plist.ts +1 -4
  157. package/src/public-brand.ts +5 -3
  158. package/src/render-mode.ts +0 -12
  159. package/src/runtime-assets.ts +9 -2
  160. package/src/screenshots/compose.ts +7 -1
  161. package/src/screenshots/frame-compose.ts +21 -10
  162. package/src/screenshots/schema.ts +1 -4
  163. package/src/sim-client.ts +2 -4
  164. package/src/skills/builtin/maestro-test.ts +1 -4
  165. package/src/sleep.ts +4 -0
  166. package/src/swift-compile.ts +209 -47
  167. package/src/swift-icu.ts +140 -0
  168. package/src/swift-package.ts +26 -2
  169. package/src/swift-submit.ts +84 -4
  170. package/src/swift.ts +2 -0
  171. package/src/vite-plugin-kotlin.ts +376 -0
  172. package/src/vite-plugin-one.ts +224 -36
  173. package/src/vite-plugin-swift.ts +382 -96
  174. package/src/vite-plugin.ts +121 -134
  175. package/src/vite.d.ts +10 -0
  176. package/cli/resolve-assets.ts +0 -63
  177. package/src/rolldown-plugin-swift.ts +0 -244
  178. package/src/team-machine-prompt.ts +0 -73
package/dist-lib/sdk.cjs CHANGED
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.577 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.579 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
  let __sootsim_import_meta_url = ''; try { __sootsim_import_meta_url = require('url').pathToFileURL(__filename).href; } catch {}
3
3
  "use strict";
4
4
  var __defProp = Object.defineProperty;
@@ -77,6 +77,7 @@ __export(sdk_exports, {
77
77
  isShellCommandUnavailable: () => isShellCommandUnavailable,
78
78
  isSimSemanticTree: () => isSimSemanticTree,
79
79
  isTapSuccess: () => isTapSuccess,
80
+ layoutBoundFontFamily: () => layoutBoundFontFamily,
80
81
  layoutGetNativeEval: () => layoutGetNativeEval,
81
82
  rankInteractive: () => rankInteractive,
82
83
  readyProbeHasContent: () => readyProbeHasContent,
@@ -89,6 +90,7 @@ __export(sdk_exports, {
89
90
  shouldSkipAutoSettleForInspectPick: () => shouldSkipAutoSettleForInspectPick,
90
91
  tapBest: () => tapBest,
91
92
  tapById: () => tapById,
93
+ tapByQuery: () => tapByQuery,
92
94
  tapByText: () => tapByText,
93
95
  tapCommandForNode: () => tapCommandForNode,
94
96
  tapCoordinates: () => tapCoordinates,
@@ -260,6 +262,24 @@ async function inspectDescribe(bridge, dumpOpts) {
260
262
  nodeCount: result.total
261
263
  };
262
264
  }
265
+ function layoutBoundFontFamily(style) {
266
+ const raw = typeof style === "object" && style !== null ? style.fontFamily : void 0;
267
+ if (typeof raw !== "string") return void 0;
268
+ const first = raw.split(",")[0].trim().replace(/^['"]|['"]$/g, "");
269
+ if (!first) return void 0;
270
+ const systemFamilies = /* @__PURE__ */ new Set([
271
+ "system",
272
+ "-apple-system",
273
+ "system-ui",
274
+ "blinkmacsystemfont",
275
+ "segoe ui",
276
+ "roboto",
277
+ "sans-serif",
278
+ "serif",
279
+ "monospace"
280
+ ]);
281
+ return systemFamilies.has(first.toLowerCase()) ? void 0 : first;
282
+ }
263
283
  function scoreLayoutTextContrast(input) {
264
284
  const required = input.fontSize >= 18 || input.fontSize >= 14 && (input.fontWeight ?? 400) >= 700 ? 3 : 4.5;
265
285
  const { normal, background } = input;
@@ -412,6 +432,7 @@ function layoutGetNativeEval(opts = {}) {
412
432
  const n = w === 'bold' ? 700 : (typeof w === 'number' ? w : (typeof w === 'string' ? parseInt(w, 10) : NaN))
413
433
  return Number.isFinite(n) && n >= 600 ? n : undefined
414
434
  }
435
+ const familyOf = ${layoutBoundFontFamily.toString()}
415
436
  const decoOf = (style) => {
416
437
  const d = style && (style.textDecorationLine || style.textDecoration)
417
438
  return d && d !== 'none' && d !== 'normal' ? String(d).split(' ')[0] : undefined
@@ -459,6 +480,27 @@ function layoutGetNativeEval(opts = {}) {
459
480
  const layout = n.layout || { x: 0, y: 0, width: 0, height: 0 }
460
481
  const abs = n.absolute || n.absolutePosition || { x: layout.x, y: layout.y }
461
482
  const text = typeof n.text === 'string' && n.text.trim() ? n.text.trim() : undefined
483
+ const rawHitSlop =
484
+ n.hitSlop != null
485
+ ? n.hitSlop
486
+ : (n.props && n.props.hitSlop != null)
487
+ ? n.props.hitSlop
488
+ : (n.style && n.style.hitSlop != null)
489
+ ? n.style.hitSlop
490
+ : (n.authoredStyle && n.authoredStyle.hitSlop != null)
491
+ ? n.authoredStyle.hitSlop
492
+ : undefined
493
+ const hitSlop =
494
+ typeof rawHitSlop === 'number'
495
+ ? (rawHitSlop !== 0 ? rawHitSlop : undefined)
496
+ : rawHitSlop && typeof rawHitSlop === 'object'
497
+ ? {
498
+ t: round(rawHitSlop.top ?? rawHitSlop.t),
499
+ r: round(rawHitSlop.right ?? rawHitSlop.r),
500
+ b: round(rawHitSlop.bottom ?? rawHitSlop.b),
501
+ l: round(rawHitSlop.left ?? rawHitSlop.l),
502
+ }
503
+ : undefined
462
504
  const base = {
463
505
  ...(typeof n.nodeId === 'number'
464
506
  ? {
@@ -473,6 +515,7 @@ function layoutGetNativeEval(opts = {}) {
473
515
  type: typeof n.type === 'string' ? n.type : 'node',
474
516
  text,
475
517
  box: { x: round(abs.x), y: round(abs.y), w: round(layout.width), h: round(layout.height) },
518
+ ...(hitSlop !== undefined ? { hitSlop } : {}),
476
519
  }
477
520
  if (!includeStyle) return base
478
521
  // computedStyle has the resolved values; style is the curated subset \u2014
@@ -489,6 +532,7 @@ function layoutGetNativeEval(opts = {}) {
489
532
  bg: colorStr(style.backgroundColor),
490
533
  opacity: opacityOf(style),
491
534
  fontWeight: weightOf(style),
535
+ fontFamily: text ? familyOf(style) : undefined,
492
536
  textDecoration: decoOf(style),
493
537
  overflow: overflowOf(style),
494
538
  borderWidth,
@@ -588,11 +632,24 @@ function formatLayoutElements(elements, opts) {
588
632
  const uniform = p.t === p.r && p.r === p.b && p.b === p.l;
589
633
  parts.push(uniform ? `pad:${p.t}` : `pad:${p.t},${p.r},${p.b},${p.l}`);
590
634
  }
635
+ if (el.hitSlop !== void 0) {
636
+ if (typeof el.hitSlop === "number") {
637
+ parts.push(`hitSlop:${el.hitSlop}`);
638
+ } else {
639
+ const hs = el.hitSlop;
640
+ const uniform = hs.t === hs.r && hs.r === hs.b && hs.b === hs.l;
641
+ parts.push(
642
+ uniform ? `hitSlop:${hs.t}` : `hitSlop:${hs.t},${hs.r},${hs.b},${hs.l}`
643
+ );
644
+ }
645
+ }
591
646
  if (el.fontSize !== void 0 && el.fontSize > 0) {
592
647
  parts.push(`font:${el.fontSize}`);
593
648
  }
594
649
  if (opts?.styling) {
595
650
  if (el.fontWeight) parts.push(`weight:${el.fontWeight}`);
651
+ if (el.fontFamily) parts.push(`family:"${el.fontFamily}"`);
652
+ if (el.fontLoaded === false) parts.push(`fontLoaded:false`);
596
653
  if (el.color) parts.push(`color:${el.color}`);
597
654
  if (el.bg) parts.push(`bg:${el.bg}`);
598
655
  if (el.contrast) {
@@ -624,6 +681,7 @@ async function inspectAccessibilityTree(bridge) {
624
681
  });
625
682
  if (!isSemanticQueryResult(result)) return [];
626
683
  return result.nodes.flatMap((node) => {
684
+ if (node.accessibilityHidden) return [];
627
685
  if (!node.role && !node.label && !node.text && !node.pressable) return [];
628
686
  return [
629
687
  {
@@ -715,7 +773,8 @@ function scoreInteractive(n) {
715
773
  function tapCommandForNode(n) {
716
774
  if (n.testID) return `peach do tap-id ${shellEscape(n.testID)}`;
717
775
  const text = typeof n.text === "string" ? n.text.trim() : "";
718
- if (text.length > 0 && text.length <= 80) return `peach do tap-text ${shellEscape(text)}`;
776
+ if (text.length > 0 && text.length <= 80)
777
+ return `peach do tap-text ${shellEscape(text)}`;
719
778
  const x = Math.round(((n.absolutePosition?.x ?? 0) + (n.layout?.width ?? 0) / 2) * 10) / 10;
720
779
  const y = Math.round(((n.absolutePosition?.y ?? 0) + (n.layout?.height ?? 0) / 2) * 10) / 10;
721
780
  return `peach do tap ${x} ${y}`;
@@ -1370,7 +1429,8 @@ async function waitForSootsimIdle({
1370
1429
  maxMs,
1371
1430
  pollMs = 50,
1372
1431
  stablePolls = 3,
1373
- strict = false
1432
+ strict = false,
1433
+ paintStill = false
1374
1434
  }) {
1375
1435
  const result = await bridge.send(
1376
1436
  {
@@ -1382,6 +1442,7 @@ async function waitForSootsimIdle({
1382
1442
  const pollMs = ${Math.max(1, Math.round(pollMs))}
1383
1443
  const requiredStablePolls = ${Math.max(1, Math.round(stablePolls))}
1384
1444
  const strict = ${strict ? "true" : "false"}
1445
+ const paintStill = ${paintStill ? "true" : "false"}
1385
1446
  const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms))
1386
1447
 
1387
1448
  // route-aware settle: a tap that pushes/pops a screen is async \u2014 the
@@ -1413,10 +1474,12 @@ async function waitForSootsimIdle({
1413
1474
  let requestTotal = 0
1414
1475
  let requestInFlight = 0
1415
1476
  let renderStatsAvailable = false
1477
+ let frameCount = -1
1416
1478
  try {
1417
1479
  const stats = await window.__sootsimRenderHost?.queryStats?.()
1418
1480
  if (stats) {
1419
1481
  renderStatsAvailable = true
1482
+ frameCount = typeof stats.frameCount === 'number' ? stats.frameCount : -1
1420
1483
  animating =
1421
1484
  stats.hasActiveAnims === true ||
1422
1485
  stats.hasActiveNativeAnimations === true ||
@@ -1449,7 +1512,7 @@ async function waitForSootsimIdle({
1449
1512
  }
1450
1513
  walk(root)
1451
1514
  }
1452
- return { layout: nodes.join(';'), animating, layoutDirty, pendingFetches, renderStatsAvailable, requestTotal, requestInFlight }
1515
+ return { layout: nodes.join(';'), frameCount, animating, layoutDirty, pendingFetches, renderStatsAvailable, requestTotal, requestInFlight }
1453
1516
  }
1454
1517
 
1455
1518
  // how long generic background network is allowed to keep us waiting AFTER
@@ -1464,6 +1527,8 @@ async function waitForSootsimIdle({
1464
1527
  // only within this grace once the screen is visually still.
1465
1528
  const networkQuietGraceMs = 1200
1466
1529
  let lastLayout = ''
1530
+ let lastFrameCount = -2
1531
+ let paintedPolls = 0
1467
1532
  let lastRequestTotal = -1
1468
1533
  let stable = 0
1469
1534
  let visuallyStillSince = 0
@@ -1480,13 +1545,18 @@ async function waitForSootsimIdle({
1480
1545
  if (snapshot.layoutDirty) dirtyPolls++
1481
1546
  if (snapshot.animating) animatingPolls++
1482
1547
  if (polls > 1 && snapshot.layout !== lastLayout) layoutChangedPolls++
1548
+ const painted = paintStill && snapshot.frameCount !== lastFrameCount
1549
+ if (polls > 1 && painted) paintedPolls++
1483
1550
  const strictOk =
1484
1551
  !strict ||
1485
1552
  (snapshot.renderStatsAvailable && !snapshot.animating && !snapshot.layoutDirty)
1486
1553
  // visually settled: no animations, no image content loading, layout
1487
1554
  // unchanged since the last poll.
1488
1555
  const visuallyStill =
1489
- strictOk && snapshot.pendingFetches === 0 && snapshot.layout === lastLayout
1556
+ strictOk &&
1557
+ !painted &&
1558
+ snapshot.pendingFetches === 0 &&
1559
+ snapshot.layout === lastLayout
1490
1560
  // generic network quiet: nothing in flight and no new requests issued.
1491
1561
  const networkQuiet =
1492
1562
  snapshot.requestInFlight === 0 && snapshot.requestTotal === lastRequestTotal
@@ -1507,6 +1577,7 @@ async function waitForSootsimIdle({
1507
1577
  visuallyStillSince = 0
1508
1578
  }
1509
1579
  lastLayout = snapshot.layout
1580
+ lastFrameCount = snapshot.frameCount
1510
1581
  lastRequestTotal = snapshot.requestTotal
1511
1582
  await sleep(pollMs)
1512
1583
  }
@@ -1517,6 +1588,7 @@ async function waitForSootsimIdle({
1517
1588
  if (last.layoutDirty) blockers.push('layout still dirty')
1518
1589
  if (last.pendingFetches > 0) blockers.push(last.pendingFetches + ' image fetches pending')
1519
1590
  if (last.layout !== lastLayout) blockers.push('layout still changing')
1591
+ if (paintStill && last.frameCount !== lastFrameCount) blockers.push('still painting')
1520
1592
  if (last.requestInFlight > 0) blockers.push(last.requestInFlight + ' requests in flight')
1521
1593
  return {
1522
1594
  settled: false,
@@ -1524,7 +1596,7 @@ async function waitForSootsimIdle({
1524
1596
  blockedBy:
1525
1597
  (blockers.length ? blockers.join(', ') : 'unknown') +
1526
1598
  ' over ' + polls + ' polls (layout changed ' + layoutChangedPolls +
1527
- 'x, layoutDirty ' + dirtyPolls + 'x, animating ' + animatingPolls + 'x)',
1599
+ 'x, layoutDirty ' + dirtyPolls + 'x, animating ' + animatingPolls + 'x, painted ' + paintedPolls + 'x)',
1528
1600
  }
1529
1601
  })()`
1530
1602
  },
@@ -1840,6 +1912,48 @@ async function tapByText(bridge, query, options = {}, opts = {}) {
1840
1912
  }
1841
1913
  });
1842
1914
  }
1915
+ var TAP_QUERY_FINDERS = {
1916
+ label: "findByLabel",
1917
+ role: "findByRole",
1918
+ hint: "findByHint",
1919
+ state: "findByA11yState"
1920
+ };
1921
+ function isTapNode(value) {
1922
+ return value !== null && typeof value === "object" && typeof Reflect.get(value, "nodeId") === "number";
1923
+ }
1924
+ function isTapTargetInfo(value) {
1925
+ return value !== null && typeof value === "object" && typeof Reflect.get(value, "cx") === "number" && typeof Reflect.get(value, "cy") === "number" && isTapNode(Reflect.get(value, "target"));
1926
+ }
1927
+ async function tapByQuery(bridge, query, opts = {}) {
1928
+ const call = (method, arg) => bridge.send({ type: "call", path: `SootSim.bridges.test.${method}`, args: [arg] });
1929
+ return tapResolvedTarget(bridge, {
1930
+ agent: opts.agent,
1931
+ timing: opts.timing,
1932
+ textFallback: typeof query.value === "string" ? query.value : void 0,
1933
+ resolve: async () => {
1934
+ const node = await call(TAP_QUERY_FINDERS[query.by], query.value);
1935
+ if (!isTapNode(node)) return { cx: null };
1936
+ const tap = await call("resolveTapTarget", node.nodeId);
1937
+ if (!isTapTargetInfo(tap)) return { cx: null };
1938
+ return payloadFromResolved(
1939
+ {
1940
+ x: tap.cx,
1941
+ y: tap.cy,
1942
+ nodeId: tap.target.nodeId,
1943
+ testID: tap.target.testID,
1944
+ text: tap.target.text ?? tap.target.accessibilityLabel,
1945
+ type: tap.target.type
1946
+ },
1947
+ {
1948
+ nodeId: node.nodeId,
1949
+ testID: node.testID,
1950
+ text: node.text ?? node.accessibilityLabel,
1951
+ type: node.type
1952
+ }
1953
+ );
1954
+ }
1955
+ });
1956
+ }
1843
1957
  async function tapBest(bridge, query, opts = {}) {
1844
1958
  return tapResolvedTarget(bridge, {
1845
1959
  agent: opts.agent,
@@ -1904,6 +2018,11 @@ async function openAppUrl(bridge, target) {
1904
2018
  return normalized;
1905
2019
  }
1906
2020
 
2021
+ // src/sleep.ts
2022
+ function sleep(ms) {
2023
+ return new Promise((resolve) => setTimeout(resolve, ms));
2024
+ }
2025
+
1907
2026
  // src/sim-client.ts
1908
2027
  var SimClientError = class extends Error {
1909
2028
  constructor(message) {
@@ -1946,9 +2065,6 @@ var SimResetError = class extends SimClientError {
1946
2065
  this.result = result;
1947
2066
  }
1948
2067
  };
1949
- function sleep(ms) {
1950
- return new Promise((resolve) => setTimeout(resolve, ms));
1951
- }
1952
2068
  function decodePngDataUrl(dataUrl) {
1953
2069
  if (typeof dataUrl !== "string" || !dataUrl.startsWith("data:image/png;base64,")) {
1954
2070
  throw new SimClientError(
@@ -1971,7 +2087,8 @@ var TARGET_SEARCH_PARAMS = [
1971
2087
  "port",
1972
2088
  "inspectOpen",
1973
2089
  "appFonts",
1974
- "appSplash"
2090
+ "appSplash",
2091
+ "appCatalog"
1975
2092
  ];
1976
2093
  function buildSimOpenUrl(currentUrl, target) {
1977
2094
  const url = new URL(currentUrl);
@@ -2412,6 +2529,7 @@ var SimClient = class {
2412
2529
  isShellCommandUnavailable,
2413
2530
  isSimSemanticTree,
2414
2531
  isTapSuccess,
2532
+ layoutBoundFontFamily,
2415
2533
  layoutGetNativeEval,
2416
2534
  rankInteractive,
2417
2535
  readyProbeHasContent,
@@ -2424,6 +2542,7 @@ var SimClient = class {
2424
2542
  shouldSkipAutoSettleForInspectPick,
2425
2543
  tapBest,
2426
2544
  tapById,
2545
+ tapByQuery,
2427
2546
  tapByText,
2428
2547
  tapCommandForNode,
2429
2548
  tapCoordinates,
package/dist-lib/sdk.mjs CHANGED
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.577 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.579 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
  var __defProp = Object.defineProperty;
3
3
  var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
4
4
  var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
@@ -164,6 +164,24 @@ async function inspectDescribe(bridge, dumpOpts) {
164
164
  nodeCount: result.total
165
165
  };
166
166
  }
167
+ function layoutBoundFontFamily(style) {
168
+ const raw = typeof style === "object" && style !== null ? style.fontFamily : void 0;
169
+ if (typeof raw !== "string") return void 0;
170
+ const first = raw.split(",")[0].trim().replace(/^['"]|['"]$/g, "");
171
+ if (!first) return void 0;
172
+ const systemFamilies = /* @__PURE__ */ new Set([
173
+ "system",
174
+ "-apple-system",
175
+ "system-ui",
176
+ "blinkmacsystemfont",
177
+ "segoe ui",
178
+ "roboto",
179
+ "sans-serif",
180
+ "serif",
181
+ "monospace"
182
+ ]);
183
+ return systemFamilies.has(first.toLowerCase()) ? void 0 : first;
184
+ }
167
185
  function scoreLayoutTextContrast(input) {
168
186
  const required = input.fontSize >= 18 || input.fontSize >= 14 && (input.fontWeight ?? 400) >= 700 ? 3 : 4.5;
169
187
  const { normal, background } = input;
@@ -316,6 +334,7 @@ function layoutGetNativeEval(opts = {}) {
316
334
  const n = w === 'bold' ? 700 : (typeof w === 'number' ? w : (typeof w === 'string' ? parseInt(w, 10) : NaN))
317
335
  return Number.isFinite(n) && n >= 600 ? n : undefined
318
336
  }
337
+ const familyOf = ${layoutBoundFontFamily.toString()}
319
338
  const decoOf = (style) => {
320
339
  const d = style && (style.textDecorationLine || style.textDecoration)
321
340
  return d && d !== 'none' && d !== 'normal' ? String(d).split(' ')[0] : undefined
@@ -363,6 +382,27 @@ function layoutGetNativeEval(opts = {}) {
363
382
  const layout = n.layout || { x: 0, y: 0, width: 0, height: 0 }
364
383
  const abs = n.absolute || n.absolutePosition || { x: layout.x, y: layout.y }
365
384
  const text = typeof n.text === 'string' && n.text.trim() ? n.text.trim() : undefined
385
+ const rawHitSlop =
386
+ n.hitSlop != null
387
+ ? n.hitSlop
388
+ : (n.props && n.props.hitSlop != null)
389
+ ? n.props.hitSlop
390
+ : (n.style && n.style.hitSlop != null)
391
+ ? n.style.hitSlop
392
+ : (n.authoredStyle && n.authoredStyle.hitSlop != null)
393
+ ? n.authoredStyle.hitSlop
394
+ : undefined
395
+ const hitSlop =
396
+ typeof rawHitSlop === 'number'
397
+ ? (rawHitSlop !== 0 ? rawHitSlop : undefined)
398
+ : rawHitSlop && typeof rawHitSlop === 'object'
399
+ ? {
400
+ t: round(rawHitSlop.top ?? rawHitSlop.t),
401
+ r: round(rawHitSlop.right ?? rawHitSlop.r),
402
+ b: round(rawHitSlop.bottom ?? rawHitSlop.b),
403
+ l: round(rawHitSlop.left ?? rawHitSlop.l),
404
+ }
405
+ : undefined
366
406
  const base = {
367
407
  ...(typeof n.nodeId === 'number'
368
408
  ? {
@@ -377,6 +417,7 @@ function layoutGetNativeEval(opts = {}) {
377
417
  type: typeof n.type === 'string' ? n.type : 'node',
378
418
  text,
379
419
  box: { x: round(abs.x), y: round(abs.y), w: round(layout.width), h: round(layout.height) },
420
+ ...(hitSlop !== undefined ? { hitSlop } : {}),
380
421
  }
381
422
  if (!includeStyle) return base
382
423
  // computedStyle has the resolved values; style is the curated subset \u2014
@@ -393,6 +434,7 @@ function layoutGetNativeEval(opts = {}) {
393
434
  bg: colorStr(style.backgroundColor),
394
435
  opacity: opacityOf(style),
395
436
  fontWeight: weightOf(style),
437
+ fontFamily: text ? familyOf(style) : undefined,
396
438
  textDecoration: decoOf(style),
397
439
  overflow: overflowOf(style),
398
440
  borderWidth,
@@ -492,11 +534,24 @@ function formatLayoutElements(elements, opts) {
492
534
  const uniform = p.t === p.r && p.r === p.b && p.b === p.l;
493
535
  parts.push(uniform ? `pad:${p.t}` : `pad:${p.t},${p.r},${p.b},${p.l}`);
494
536
  }
537
+ if (el.hitSlop !== void 0) {
538
+ if (typeof el.hitSlop === "number") {
539
+ parts.push(`hitSlop:${el.hitSlop}`);
540
+ } else {
541
+ const hs = el.hitSlop;
542
+ const uniform = hs.t === hs.r && hs.r === hs.b && hs.b === hs.l;
543
+ parts.push(
544
+ uniform ? `hitSlop:${hs.t}` : `hitSlop:${hs.t},${hs.r},${hs.b},${hs.l}`
545
+ );
546
+ }
547
+ }
495
548
  if (el.fontSize !== void 0 && el.fontSize > 0) {
496
549
  parts.push(`font:${el.fontSize}`);
497
550
  }
498
551
  if (opts?.styling) {
499
552
  if (el.fontWeight) parts.push(`weight:${el.fontWeight}`);
553
+ if (el.fontFamily) parts.push(`family:"${el.fontFamily}"`);
554
+ if (el.fontLoaded === false) parts.push(`fontLoaded:false`);
500
555
  if (el.color) parts.push(`color:${el.color}`);
501
556
  if (el.bg) parts.push(`bg:${el.bg}`);
502
557
  if (el.contrast) {
@@ -528,6 +583,7 @@ async function inspectAccessibilityTree(bridge) {
528
583
  });
529
584
  if (!isSemanticQueryResult(result)) return [];
530
585
  return result.nodes.flatMap((node) => {
586
+ if (node.accessibilityHidden) return [];
531
587
  if (!node.role && !node.label && !node.text && !node.pressable) return [];
532
588
  return [
533
589
  {
@@ -619,7 +675,8 @@ function scoreInteractive(n) {
619
675
  function tapCommandForNode(n) {
620
676
  if (n.testID) return `peach do tap-id ${shellEscape(n.testID)}`;
621
677
  const text = typeof n.text === "string" ? n.text.trim() : "";
622
- if (text.length > 0 && text.length <= 80) return `peach do tap-text ${shellEscape(text)}`;
678
+ if (text.length > 0 && text.length <= 80)
679
+ return `peach do tap-text ${shellEscape(text)}`;
623
680
  const x = Math.round(((n.absolutePosition?.x ?? 0) + (n.layout?.width ?? 0) / 2) * 10) / 10;
624
681
  const y = Math.round(((n.absolutePosition?.y ?? 0) + (n.layout?.height ?? 0) / 2) * 10) / 10;
625
682
  return `peach do tap ${x} ${y}`;
@@ -1274,7 +1331,8 @@ async function waitForSootsimIdle({
1274
1331
  maxMs,
1275
1332
  pollMs = 50,
1276
1333
  stablePolls = 3,
1277
- strict = false
1334
+ strict = false,
1335
+ paintStill = false
1278
1336
  }) {
1279
1337
  const result = await bridge.send(
1280
1338
  {
@@ -1286,6 +1344,7 @@ async function waitForSootsimIdle({
1286
1344
  const pollMs = ${Math.max(1, Math.round(pollMs))}
1287
1345
  const requiredStablePolls = ${Math.max(1, Math.round(stablePolls))}
1288
1346
  const strict = ${strict ? "true" : "false"}
1347
+ const paintStill = ${paintStill ? "true" : "false"}
1289
1348
  const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms))
1290
1349
 
1291
1350
  // route-aware settle: a tap that pushes/pops a screen is async \u2014 the
@@ -1317,10 +1376,12 @@ async function waitForSootsimIdle({
1317
1376
  let requestTotal = 0
1318
1377
  let requestInFlight = 0
1319
1378
  let renderStatsAvailable = false
1379
+ let frameCount = -1
1320
1380
  try {
1321
1381
  const stats = await window.__sootsimRenderHost?.queryStats?.()
1322
1382
  if (stats) {
1323
1383
  renderStatsAvailable = true
1384
+ frameCount = typeof stats.frameCount === 'number' ? stats.frameCount : -1
1324
1385
  animating =
1325
1386
  stats.hasActiveAnims === true ||
1326
1387
  stats.hasActiveNativeAnimations === true ||
@@ -1353,7 +1414,7 @@ async function waitForSootsimIdle({
1353
1414
  }
1354
1415
  walk(root)
1355
1416
  }
1356
- return { layout: nodes.join(';'), animating, layoutDirty, pendingFetches, renderStatsAvailable, requestTotal, requestInFlight }
1417
+ return { layout: nodes.join(';'), frameCount, animating, layoutDirty, pendingFetches, renderStatsAvailable, requestTotal, requestInFlight }
1357
1418
  }
1358
1419
 
1359
1420
  // how long generic background network is allowed to keep us waiting AFTER
@@ -1368,6 +1429,8 @@ async function waitForSootsimIdle({
1368
1429
  // only within this grace once the screen is visually still.
1369
1430
  const networkQuietGraceMs = 1200
1370
1431
  let lastLayout = ''
1432
+ let lastFrameCount = -2
1433
+ let paintedPolls = 0
1371
1434
  let lastRequestTotal = -1
1372
1435
  let stable = 0
1373
1436
  let visuallyStillSince = 0
@@ -1384,13 +1447,18 @@ async function waitForSootsimIdle({
1384
1447
  if (snapshot.layoutDirty) dirtyPolls++
1385
1448
  if (snapshot.animating) animatingPolls++
1386
1449
  if (polls > 1 && snapshot.layout !== lastLayout) layoutChangedPolls++
1450
+ const painted = paintStill && snapshot.frameCount !== lastFrameCount
1451
+ if (polls > 1 && painted) paintedPolls++
1387
1452
  const strictOk =
1388
1453
  !strict ||
1389
1454
  (snapshot.renderStatsAvailable && !snapshot.animating && !snapshot.layoutDirty)
1390
1455
  // visually settled: no animations, no image content loading, layout
1391
1456
  // unchanged since the last poll.
1392
1457
  const visuallyStill =
1393
- strictOk && snapshot.pendingFetches === 0 && snapshot.layout === lastLayout
1458
+ strictOk &&
1459
+ !painted &&
1460
+ snapshot.pendingFetches === 0 &&
1461
+ snapshot.layout === lastLayout
1394
1462
  // generic network quiet: nothing in flight and no new requests issued.
1395
1463
  const networkQuiet =
1396
1464
  snapshot.requestInFlight === 0 && snapshot.requestTotal === lastRequestTotal
@@ -1411,6 +1479,7 @@ async function waitForSootsimIdle({
1411
1479
  visuallyStillSince = 0
1412
1480
  }
1413
1481
  lastLayout = snapshot.layout
1482
+ lastFrameCount = snapshot.frameCount
1414
1483
  lastRequestTotal = snapshot.requestTotal
1415
1484
  await sleep(pollMs)
1416
1485
  }
@@ -1421,6 +1490,7 @@ async function waitForSootsimIdle({
1421
1490
  if (last.layoutDirty) blockers.push('layout still dirty')
1422
1491
  if (last.pendingFetches > 0) blockers.push(last.pendingFetches + ' image fetches pending')
1423
1492
  if (last.layout !== lastLayout) blockers.push('layout still changing')
1493
+ if (paintStill && last.frameCount !== lastFrameCount) blockers.push('still painting')
1424
1494
  if (last.requestInFlight > 0) blockers.push(last.requestInFlight + ' requests in flight')
1425
1495
  return {
1426
1496
  settled: false,
@@ -1428,7 +1498,7 @@ async function waitForSootsimIdle({
1428
1498
  blockedBy:
1429
1499
  (blockers.length ? blockers.join(', ') : 'unknown') +
1430
1500
  ' over ' + polls + ' polls (layout changed ' + layoutChangedPolls +
1431
- 'x, layoutDirty ' + dirtyPolls + 'x, animating ' + animatingPolls + 'x)',
1501
+ 'x, layoutDirty ' + dirtyPolls + 'x, animating ' + animatingPolls + 'x, painted ' + paintedPolls + 'x)',
1432
1502
  }
1433
1503
  })()`
1434
1504
  },
@@ -1744,6 +1814,48 @@ async function tapByText(bridge, query, options = {}, opts = {}) {
1744
1814
  }
1745
1815
  });
1746
1816
  }
1817
+ var TAP_QUERY_FINDERS = {
1818
+ label: "findByLabel",
1819
+ role: "findByRole",
1820
+ hint: "findByHint",
1821
+ state: "findByA11yState"
1822
+ };
1823
+ function isTapNode(value) {
1824
+ return value !== null && typeof value === "object" && typeof Reflect.get(value, "nodeId") === "number";
1825
+ }
1826
+ function isTapTargetInfo(value) {
1827
+ return value !== null && typeof value === "object" && typeof Reflect.get(value, "cx") === "number" && typeof Reflect.get(value, "cy") === "number" && isTapNode(Reflect.get(value, "target"));
1828
+ }
1829
+ async function tapByQuery(bridge, query, opts = {}) {
1830
+ const call = (method, arg) => bridge.send({ type: "call", path: `SootSim.bridges.test.${method}`, args: [arg] });
1831
+ return tapResolvedTarget(bridge, {
1832
+ agent: opts.agent,
1833
+ timing: opts.timing,
1834
+ textFallback: typeof query.value === "string" ? query.value : void 0,
1835
+ resolve: async () => {
1836
+ const node = await call(TAP_QUERY_FINDERS[query.by], query.value);
1837
+ if (!isTapNode(node)) return { cx: null };
1838
+ const tap = await call("resolveTapTarget", node.nodeId);
1839
+ if (!isTapTargetInfo(tap)) return { cx: null };
1840
+ return payloadFromResolved(
1841
+ {
1842
+ x: tap.cx,
1843
+ y: tap.cy,
1844
+ nodeId: tap.target.nodeId,
1845
+ testID: tap.target.testID,
1846
+ text: tap.target.text ?? tap.target.accessibilityLabel,
1847
+ type: tap.target.type
1848
+ },
1849
+ {
1850
+ nodeId: node.nodeId,
1851
+ testID: node.testID,
1852
+ text: node.text ?? node.accessibilityLabel,
1853
+ type: node.type
1854
+ }
1855
+ );
1856
+ }
1857
+ });
1858
+ }
1747
1859
  async function tapBest(bridge, query, opts = {}) {
1748
1860
  return tapResolvedTarget(bridge, {
1749
1861
  agent: opts.agent,
@@ -1808,6 +1920,11 @@ async function openAppUrl(bridge, target) {
1808
1920
  return normalized;
1809
1921
  }
1810
1922
 
1923
+ // src/sleep.ts
1924
+ function sleep(ms) {
1925
+ return new Promise((resolve) => setTimeout(resolve, ms));
1926
+ }
1927
+
1811
1928
  // src/sim-client.ts
1812
1929
  var SimClientError = class extends Error {
1813
1930
  constructor(message) {
@@ -1850,9 +1967,6 @@ var SimResetError = class extends SimClientError {
1850
1967
  this.result = result;
1851
1968
  }
1852
1969
  };
1853
- function sleep(ms) {
1854
- return new Promise((resolve) => setTimeout(resolve, ms));
1855
- }
1856
1970
  function decodePngDataUrl(dataUrl) {
1857
1971
  if (typeof dataUrl !== "string" || !dataUrl.startsWith("data:image/png;base64,")) {
1858
1972
  throw new SimClientError(
@@ -1875,7 +1989,8 @@ var TARGET_SEARCH_PARAMS = [
1875
1989
  "port",
1876
1990
  "inspectOpen",
1877
1991
  "appFonts",
1878
- "appSplash"
1992
+ "appSplash",
1993
+ "appCatalog"
1879
1994
  ];
1880
1995
  function buildSimOpenUrl(currentUrl, target) {
1881
1996
  const url = new URL(currentUrl);
@@ -2315,6 +2430,7 @@ export {
2315
2430
  isShellCommandUnavailable,
2316
2431
  isSimSemanticTree,
2317
2432
  isTapSuccess,
2433
+ layoutBoundFontFamily,
2318
2434
  layoutGetNativeEval,
2319
2435
  rankInteractive,
2320
2436
  readyProbeHasContent,
@@ -2327,6 +2443,7 @@ export {
2327
2443
  shouldSkipAutoSettleForInspectPick,
2328
2444
  tapBest,
2329
2445
  tapById,
2446
+ tapByQuery,
2330
2447
  tapByText,
2331
2448
  tapCommandForNode,
2332
2449
  tapCoordinates,