rnxsim 0.1.415 → 0.1.417

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 (50) hide show
  1. package/detox/index.ts +81 -8
  2. package/dist-lib/agent-daemon-client.cjs +1 -1
  3. package/dist-lib/agent-events.cjs +1 -1
  4. package/dist-lib/agent-identity.cjs +1 -1
  5. package/dist-lib/agent-sessions.cjs +1 -1
  6. package/dist-lib/attached-projects.cjs +1 -1
  7. package/dist-lib/auth/shared-session.cjs +1 -1
  8. package/dist-lib/backend-origin.cjs +1 -1
  9. package/dist-lib/beta.cjs +1 -1
  10. package/dist-lib/beta.mjs +1 -1
  11. package/dist-lib/bridge-constants.cjs +1 -1
  12. package/dist-lib/bridge-contract-input.cjs +1 -1
  13. package/dist-lib/bridge-contract-input.mjs +1 -1
  14. package/dist-lib/bridge-contract.cjs +1 -1
  15. package/dist-lib/bridge-contract.mjs +1 -1
  16. package/dist-lib/capture-contract.cjs +1 -1
  17. package/dist-lib/capture-contract.mjs +1 -1
  18. package/dist-lib/cli-constants.cjs +1 -1
  19. package/dist-lib/cloud-contract.cjs +1 -1
  20. package/dist-lib/cloud-contract.mjs +1 -1
  21. package/dist-lib/config.cjs +1 -1
  22. package/dist-lib/detox/index.cjs +55 -8
  23. package/dist-lib/dev-bundle-resolution.cjs +1 -1
  24. package/dist-lib/home-paths.cjs +1 -1
  25. package/dist-lib/host/bridge-host.cjs +1 -1
  26. package/dist-lib/host/fetch-proxy-handler.cjs +1 -1
  27. package/dist-lib/host/fetch-proxy-overrides.cjs +1 -1
  28. package/dist-lib/host/fetch-proxy-overrides.mjs +1 -1
  29. package/dist-lib/host/replacement-module-handler.cjs +1 -1
  30. package/dist-lib/host/websocket-proxy.cjs +1 -1
  31. package/dist-lib/index.cjs +1 -1
  32. package/dist-lib/jump-to-source-babel.cjs +1 -1
  33. package/dist-lib/menu.cjs +1 -1
  34. package/dist-lib/menu.mjs +1 -1
  35. package/dist-lib/metro-fingerprint-registry.cjs +1 -1
  36. package/dist-lib/metro-fingerprint-registry.mjs +1 -1
  37. package/dist-lib/metro-production-bundle.cjs +1 -1
  38. package/dist-lib/metro-production-bundle.mjs +1 -1
  39. package/dist-lib/metro.cjs +1 -1
  40. package/dist-lib/profiles.cjs +1 -1
  41. package/dist-lib/public-brand.cjs +1 -1
  42. package/dist-lib/react-native-host-modules.cjs +1 -1
  43. package/dist-lib/react-native-host-modules.mjs +1 -1
  44. package/dist-lib/render-mode.cjs +1 -1
  45. package/dist-lib/scripts/dev-server-scanner.cjs +1 -1
  46. package/dist-lib/sdk.cjs +1 -1
  47. package/dist-lib/sdk.mjs +1 -1
  48. package/dist-lib/skills.cjs +1 -1
  49. package/dist-lib/vite.cjs +1 -1
  50. package/package.json +1 -1
package/detox/index.ts CHANGED
@@ -779,12 +779,24 @@ function createSootSimElement(matcher: Matcher): SootSimElement {
779
779
  await page.waitForTimeout(80)
780
780
  },
781
781
 
782
- async longPress(duration = 1000) {
782
+ // mirrors detox element.longPress(duration) and longPress(point, duration),
783
+ // where the point is relative to the element's origin
784
+ async longPress(
785
+ pointOrDuration?: { x: number; y: number } | number,
786
+ durationArg = 1000,
787
+ ) {
783
788
  const page = getPage()
784
789
  const node = await findNodeByMatcher(matcher)
785
790
  if (!node)
786
791
  throw new Error(`element not found for longPress: ${JSON.stringify(matcher)}`)
787
- const center = getNodeCenter(node)
792
+ const duration = typeof pointOrDuration === 'number' ? pointOrDuration : durationArg
793
+ const target =
794
+ typeof pointOrDuration === 'object'
795
+ ? {
796
+ x: node.absolutePosition.x + pointOrDuration.x,
797
+ y: node.absolutePosition.y + pointOrDuration.y,
798
+ }
799
+ : getNodeCenter(node)
788
800
  await page.evaluate(
789
801
  async ({ duration, x, y }) => {
790
802
  const longPress = window.SootSim?.bridges?.interact?.longPress
@@ -796,7 +808,7 @@ function createSootSimElement(matcher: Matcher): SootSimElement {
796
808
  throw new Error(`sootsim longPress missed at ${x},${y}`)
797
809
  }
798
810
  },
799
- { duration, x: center.x, y: center.y },
811
+ { duration, x: target.x, y: target.y },
800
812
  )
801
813
  await page.waitForTimeout(50)
802
814
  },
@@ -1548,8 +1560,14 @@ export const device = {
1548
1560
  options: {
1549
1561
  detectionTimeoutMs?: number
1550
1562
  frame?: { height: number; width: number; x: number; y: number } | null
1563
+ // a pointer the capture itself presses at the trigger point and releases
1564
+ // once durationMs of lockstep time has elapsed, so held states (a lifted
1565
+ // control thumb, a pressed row) are painted mid-hold. an external trigger
1566
+ // that holds and releases before returning never paints a frame in
1567
+ // between, because no lockstep frame steps while it runs.
1568
+ hold?: { durationMs: number; x: number; y: number } | null
1551
1569
  rootFrame?: { height: number; width: number; x: number; y: number } | null
1552
- trigger: () => unknown
1570
+ trigger?: () => unknown
1553
1571
  },
1554
1572
  ): Promise<{
1555
1573
  frames: Array<{
@@ -1618,6 +1636,10 @@ export const device = {
1618
1636
  __sootsimVisibleChangeCaptures?: Record<string, CaptureState>
1619
1637
  SootSim?: {
1620
1638
  bridges?: {
1639
+ interact?: {
1640
+ touchDown: (x: number, y: number) => Promise<boolean>
1641
+ touchUp: (x: number, y: number) => Promise<boolean>
1642
+ }
1621
1643
  liveComposite?: {
1622
1644
  captureFresh: () => Promise<{
1623
1645
  canvas: HTMLCanvasElement | null
@@ -1740,15 +1762,48 @@ export const device = {
1740
1762
  }
1741
1763
  }
1742
1764
 
1765
+ const interact = captureWindow.SootSim?.bridges?.interact
1766
+ if (input.hold && (!interact || typeof interact.touchDown !== 'function')) {
1767
+ throw new Error('rnx interact bridge is not installed')
1768
+ }
1769
+ const frameMs = 1000 / 60
1743
1770
  const controller = await lockstep.begin(60)
1771
+ // the hold's pointer goes down before the first stepped frame, so its
1772
+ // start is one frame before the first receipt; the release lands on the
1773
+ // first stepped frame at or past durationMs from there. the deadline is
1774
+ // a frame index: comparing summed virtual timestamps against the
1775
+ // duration lets rounding push the release past the frame it belongs to.
1776
+ const holdState: { released: boolean; releaseFrameIndex: number | null } = {
1777
+ released: false,
1778
+ releaseFrameIndex: null,
1779
+ }
1780
+ const releaseHoldIfDue = async (receipt: FrameReceipt) => {
1781
+ if (!input.hold || !interact || holdState.released) return
1782
+ if (holdState.releaseFrameIndex === null) {
1783
+ holdState.releaseFrameIndex =
1784
+ receipt.frameIndex - 1 + Math.ceil(input.hold.durationMs / frameMs)
1785
+ }
1786
+ if (receipt.frameIndex < holdState.releaseFrameIndex) return
1787
+ holdState.released = true
1788
+ await interact.touchUp(input.hold.x, input.hold.y)
1789
+ }
1744
1790
  try {
1745
1791
  const baselineBitmap = await captureBitmap()
1746
1792
  const baseline = regionPixels(baselineBitmap)
1747
1793
  baselineBitmap.close()
1748
1794
  state.ready = true
1749
- while (!state.triggerCompleted) {
1750
- if (state.cancelled) throw new Error('rnx motion capture was cancelled')
1751
- await new Promise((resolve) => setTimeout(resolve, 0))
1795
+ if (input.hold && interact) {
1796
+ const hit = await interact.touchDown(input.hold.x, input.hold.y)
1797
+ if (!hit) {
1798
+ throw new Error(
1799
+ `rnx motion hold missed at ${input.hold.x},${input.hold.y}`,
1800
+ )
1801
+ }
1802
+ } else {
1803
+ while (!state.triggerCompleted) {
1804
+ if (state.cancelled) throw new Error('rnx motion capture was cancelled')
1805
+ await new Promise((resolve) => setTimeout(resolve, 0))
1806
+ }
1752
1807
  }
1753
1808
 
1754
1809
  let visibleStart: FrameReceipt | null = null
@@ -1757,6 +1812,7 @@ export const device = {
1757
1812
  for (let frame = 0; frame < detectionFrameLimit; frame++) {
1758
1813
  if (state.cancelled) throw new Error('rnx motion capture was cancelled')
1759
1814
  const receipt = await controller.stepFrame()
1815
+ await releaseHoldIfDue(receipt)
1760
1816
  const candidateBitmap = await captureBitmap()
1761
1817
  const bounds = boundsFor(candidateBitmap)
1762
1818
  const measurement = measureChange({
@@ -1783,7 +1839,18 @@ export const device = {
1783
1839
  const snapshots: Snapshot[] = []
1784
1840
  for (const stage of input.stages) {
1785
1841
  if (state.cancelled) throw new Error('rnx motion capture was cancelled')
1842
+ if (input.hold && !holdState.released && holdState.releaseFrameIndex !== null) {
1843
+ // step to the release frame first when this stage lies past it,
1844
+ // so the stage paints the released state rather than a hold
1845
+ // that overran because the jump skipped its deadline.
1846
+ const stageFrameIndex =
1847
+ visibleStart.frameIndex + Math.ceil(stage.offsetMs / frameMs)
1848
+ while (!holdState.released && stageFrameIndex > holdState.releaseFrameIndex) {
1849
+ await releaseHoldIfDue(await controller.stepFrame())
1850
+ }
1851
+ }
1786
1852
  const receipt = await controller.stepToOffset(visibleStart, stage.offsetMs)
1853
+ await releaseHoldIfDue(receipt)
1787
1854
  snapshots.push({
1788
1855
  actualMs: receipt.virtualTimestampMs - visibleStart.virtualTimestampMs,
1789
1856
  bitmap: await captureBitmap(),
@@ -1814,6 +1881,7 @@ export const device = {
1814
1881
  {
1815
1882
  detectionTimeoutMs: options.detectionTimeoutMs ?? 3000,
1816
1883
  frame: options.frame ?? null,
1884
+ hold: options.hold ?? null,
1817
1885
  motionChangeDefinition: MOTION_CHANGE_DEFINITION,
1818
1886
  rootFrame: options.rootFrame ?? null,
1819
1887
  stages,
@@ -1840,7 +1908,12 @@ export const device = {
1840
1908
  const triggerStartMs = Date.now()
1841
1909
  let triggerCompletedMs = 0
1842
1910
  try {
1843
- await Promise.resolve(options.trigger())
1911
+ if (!options.hold) {
1912
+ if (typeof options.trigger !== 'function') {
1913
+ throw new Error('rnx motion capture needs a trigger or a hold')
1914
+ }
1915
+ await Promise.resolve(options.trigger())
1916
+ }
1844
1917
  triggerCompletedMs = Date.now()
1845
1918
  await page.evaluate((captureToken) => {
1846
1919
  const registry = (
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.415 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.417 | (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 __create = Object.create;
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.415 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.417 | (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;
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.415 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.417 | (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;
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.415 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.417 | (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 __create = Object.create;
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.415 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.417 | (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 __create = Object.create;
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.415 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.417 | (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 __create = Object.create;
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.415 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.417 | (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;
package/dist-lib/beta.cjs CHANGED
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.415 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.417 | (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;
package/dist-lib/beta.mjs CHANGED
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.415 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.417 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
 
3
3
  // src/beta.ts
4
4
  var IS_BETA = true;
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.415 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.417 | (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;
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.415 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.417 | (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;
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.415 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.417 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
 
3
3
  // src/bridge-contract.ts
4
4
  var SIM_LONG_PRESS_MAX_MS = 5e3;
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.415 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.417 | (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;
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.415 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.417 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
 
3
3
  // src/bridge-contract.ts
4
4
  var SIM_LONG_PRESS_DEFAULT_MS = 500;
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.415 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.417 | (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;
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.415 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.417 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
 
3
3
  // src/capture-contract.ts
4
4
  var RNX_SCREEN_CAPTURE_VERSION = 1;
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.415 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.417 | (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;
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.415 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.417 | (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;
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.415 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.417 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
 
3
3
  // src/cloud-contract.ts
4
4
  var RNX_CLOUD_MAX_ARTIFACT_BYTES = 20 * 1024 * 1024;
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.415 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.417 | (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;
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.415 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.417 | (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 __create = Object.create;
@@ -2275,12 +2275,18 @@ function createSootSimElement(matcher) {
2275
2275
  }
2276
2276
  await page.waitForTimeout(80);
2277
2277
  },
2278
- async longPress(duration = 1e3) {
2278
+ // mirrors detox element.longPress(duration) and longPress(point, duration),
2279
+ // where the point is relative to the element's origin
2280
+ async longPress(pointOrDuration, durationArg = 1e3) {
2279
2281
  const page = getPage();
2280
2282
  const node = await findNodeByMatcher(matcher);
2281
2283
  if (!node)
2282
2284
  throw new Error(`element not found for longPress: ${JSON.stringify(matcher)}`);
2283
- const center = getNodeCenter(node);
2285
+ const duration = typeof pointOrDuration === "number" ? pointOrDuration : durationArg;
2286
+ const target = typeof pointOrDuration === "object" ? {
2287
+ x: node.absolutePosition.x + pointOrDuration.x,
2288
+ y: node.absolutePosition.y + pointOrDuration.y
2289
+ } : getNodeCenter(node);
2284
2290
  await page.evaluate(
2285
2291
  async ({ duration: duration2, x, y }) => {
2286
2292
  const longPress = window.SootSim?.bridges?.interact?.longPress;
@@ -2292,7 +2298,7 @@ function createSootSimElement(matcher) {
2292
2298
  throw new Error(`sootsim longPress missed at ${x},${y}`);
2293
2299
  }
2294
2300
  },
2295
- { duration, x: center.x, y: center.y }
2301
+ { duration, x: target.x, y: target.y }
2296
2302
  );
2297
2303
  await page.waitForTimeout(50);
2298
2304
  },
@@ -2908,15 +2914,42 @@ var device = {
2908
2914
  virtualTimestampMs: snapshot.virtualTimestampMs
2909
2915
  };
2910
2916
  };
2917
+ const interact = captureWindow.SootSim?.bridges?.interact;
2918
+ if (input.hold && (!interact || typeof interact.touchDown !== "function")) {
2919
+ throw new Error("rnx interact bridge is not installed");
2920
+ }
2921
+ const frameMs = 1e3 / 60;
2911
2922
  const controller = await lockstep.begin(60);
2923
+ const holdState = {
2924
+ released: false,
2925
+ releaseFrameIndex: null
2926
+ };
2927
+ const releaseHoldIfDue = async (receipt) => {
2928
+ if (!input.hold || !interact || holdState.released) return;
2929
+ if (holdState.releaseFrameIndex === null) {
2930
+ holdState.releaseFrameIndex = receipt.frameIndex - 1 + Math.ceil(input.hold.durationMs / frameMs);
2931
+ }
2932
+ if (receipt.frameIndex < holdState.releaseFrameIndex) return;
2933
+ holdState.released = true;
2934
+ await interact.touchUp(input.hold.x, input.hold.y);
2935
+ };
2912
2936
  try {
2913
2937
  const baselineBitmap = await captureBitmap();
2914
2938
  const baseline = regionPixels(baselineBitmap);
2915
2939
  baselineBitmap.close();
2916
2940
  state.ready = true;
2917
- while (!state.triggerCompleted) {
2918
- if (state.cancelled) throw new Error("rnx motion capture was cancelled");
2919
- await new Promise((resolve) => setTimeout(resolve, 0));
2941
+ if (input.hold && interact) {
2942
+ const hit = await interact.touchDown(input.hold.x, input.hold.y);
2943
+ if (!hit) {
2944
+ throw new Error(
2945
+ `rnx motion hold missed at ${input.hold.x},${input.hold.y}`
2946
+ );
2947
+ }
2948
+ } else {
2949
+ while (!state.triggerCompleted) {
2950
+ if (state.cancelled) throw new Error("rnx motion capture was cancelled");
2951
+ await new Promise((resolve) => setTimeout(resolve, 0));
2952
+ }
2920
2953
  }
2921
2954
  let visibleStart = null;
2922
2955
  let detectedMeasurement = null;
@@ -2924,6 +2957,7 @@ var device = {
2924
2957
  for (let frame = 0; frame < detectionFrameLimit; frame++) {
2925
2958
  if (state.cancelled) throw new Error("rnx motion capture was cancelled");
2926
2959
  const receipt = await controller.stepFrame();
2960
+ await releaseHoldIfDue(receipt);
2927
2961
  const candidateBitmap = await captureBitmap();
2928
2962
  const bounds = boundsFor(candidateBitmap);
2929
2963
  const measurement = measureChange({
@@ -2949,7 +2983,14 @@ var device = {
2949
2983
  const snapshots = [];
2950
2984
  for (const stage of input.stages) {
2951
2985
  if (state.cancelled) throw new Error("rnx motion capture was cancelled");
2986
+ if (input.hold && !holdState.released && holdState.releaseFrameIndex !== null) {
2987
+ const stageFrameIndex = visibleStart.frameIndex + Math.ceil(stage.offsetMs / frameMs);
2988
+ while (!holdState.released && stageFrameIndex > holdState.releaseFrameIndex) {
2989
+ await releaseHoldIfDue(await controller.stepFrame());
2990
+ }
2991
+ }
2952
2992
  const receipt = await controller.stepToOffset(visibleStart, stage.offsetMs);
2993
+ await releaseHoldIfDue(receipt);
2953
2994
  snapshots.push({
2954
2995
  actualMs: receipt.virtualTimestampMs - visibleStart.virtualTimestampMs,
2955
2996
  bitmap: await captureBitmap(),
@@ -2980,6 +3021,7 @@ var device = {
2980
3021
  {
2981
3022
  detectionTimeoutMs: options.detectionTimeoutMs ?? 3e3,
2982
3023
  frame: options.frame ?? null,
3024
+ hold: options.hold ?? null,
2983
3025
  motionChangeDefinition: import_motion_change.MOTION_CHANGE_DEFINITION,
2984
3026
  rootFrame: options.rootFrame ?? null,
2985
3027
  stages,
@@ -2997,7 +3039,12 @@ var device = {
2997
3039
  const triggerStartMs = Date.now();
2998
3040
  let triggerCompletedMs = 0;
2999
3041
  try {
3000
- await Promise.resolve(options.trigger());
3042
+ if (!options.hold) {
3043
+ if (typeof options.trigger !== "function") {
3044
+ throw new Error("rnx motion capture needs a trigger or a hold");
3045
+ }
3046
+ await Promise.resolve(options.trigger());
3047
+ }
3001
3048
  triggerCompletedMs = Date.now();
3002
3049
  await page.evaluate((captureToken) => {
3003
3050
  const registry = window.__sootsimVisibleChangeCaptures;
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.415 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.417 | (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;
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.415 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.417 | (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 __create = Object.create;
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.415 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.417 | (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 __create = Object.create;
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.415 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.417 | (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 __create = Object.create;
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.415 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.417 | (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;
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.415 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.417 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
 
3
3
  // src/host/fetch-proxy-overrides.ts
4
4
  var FETCH_PROXY_BROWSER_USER_AGENT = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36";
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.415 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.417 | (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 __create = Object.create;
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.415 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.417 | (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;
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.415 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.417 | (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 __create = Object.create;
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.415 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.417 | (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;
package/dist-lib/menu.cjs CHANGED
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.415 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.417 | (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;
package/dist-lib/menu.mjs CHANGED
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.415 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.417 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
 
3
3
  // src/public-brand.ts
4
4
  var name = "rnx";
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.415 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.417 | (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;
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.415 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.417 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
 
3
3
  // src/metro-fingerprint-registry.ts
4
4
  var RNX_METRO_FINGERPRINT_SCHEMA_VERSION = 2;
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.415 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.417 | (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;
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.415 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.417 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
 
3
3
  // src/metro-production-bundle.ts
4
4
  var RNXSIM_METRO_MODULE_PATHS_PREFIX = "globalThis.__sootsimModulePaths=";
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.415 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.417 | (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 __create = Object.create;
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.415 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.417 | (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 __create = Object.create;
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.415 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.417 | (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;
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.415 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.417 | (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;
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.415 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.417 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
 
3
3
  // src/react-native-host-modules.ts
4
4
  var REACT_NATIVE_PASSTHROUGH_SPECIFIERS = [
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.415 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.417 | (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;
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.415 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.417 | (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 __create = Object.create;
package/dist-lib/sdk.cjs CHANGED
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.415 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.417 | (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;
package/dist-lib/sdk.mjs CHANGED
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.415 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.417 | (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);
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.415 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.417 | (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 __create = Object.create;
package/dist-lib/vite.cjs CHANGED
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.415 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.417 | (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 __create = Object.create;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rnxsim",
3
- "version": "0.1.415",
3
+ "version": "0.1.417",
4
4
  "description": "Vite and Metro plugins, testing drivers, and SDK exports for rnx.",
5
5
  "author": "Tamagui LLC",
6
6
  "license": "SEE LICENSE IN LICENSE",