rnxsim 0.1.505 → 0.1.507

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 (63) hide show
  1. package/cli/bridge-flow-runner.ts +7 -2
  2. package/cli/commands/agent-wrapper.ts +7 -3
  3. package/cli/commands/control.ts +3 -3
  4. package/cli/commands/flow.ts +9 -12
  5. package/cli/commands/inspect.ts +2 -2
  6. package/cli/commands/platform.ts +2 -1
  7. package/cli/flow-live-status.ts +8 -2
  8. package/cli/ws-bridge.ts +28 -15
  9. package/detox/index.ts +24 -0
  10. package/dist-lib/agent-daemon-client.cjs +1 -1
  11. package/dist-lib/agent-events.cjs +1 -1
  12. package/dist-lib/agent-identity.cjs +1 -1
  13. package/dist-lib/agent-sessions.cjs +1 -1
  14. package/dist-lib/attached-projects.cjs +1 -1
  15. package/dist-lib/auth/shared-session.cjs +1 -1
  16. package/dist-lib/backend-origin.cjs +1 -1
  17. package/dist-lib/beta.cjs +1 -1
  18. package/dist-lib/beta.mjs +1 -1
  19. package/dist-lib/bridge-constants.cjs +1 -1
  20. package/dist-lib/bridge-contract-input.cjs +1 -1
  21. package/dist-lib/bridge-contract-input.mjs +1 -1
  22. package/dist-lib/bridge-contract.cjs +1 -1
  23. package/dist-lib/bridge-contract.mjs +1 -1
  24. package/dist-lib/capture-contract.cjs +1 -1
  25. package/dist-lib/capture-contract.mjs +1 -1
  26. package/dist-lib/cli-constants.cjs +1 -1
  27. package/dist-lib/cloud-contract.cjs +1 -1
  28. package/dist-lib/cloud-contract.mjs +1 -1
  29. package/dist-lib/cloud.cjs +1 -1
  30. package/dist-lib/cloud.mjs +1 -1
  31. package/dist-lib/config.cjs +1 -1
  32. package/dist-lib/detox/index.cjs +24 -1
  33. package/dist-lib/dev-bundle-resolution.cjs +1 -1
  34. package/dist-lib/home-paths.cjs +1 -1
  35. package/dist-lib/host/bridge-host.cjs +142 -56
  36. package/dist-lib/host/fetch-proxy-handler.cjs +1 -1
  37. package/dist-lib/host/fetch-proxy-overrides.cjs +1 -1
  38. package/dist-lib/host/fetch-proxy-overrides.mjs +1 -1
  39. package/dist-lib/host/replacement-module-handler.cjs +1 -1
  40. package/dist-lib/host/websocket-proxy.cjs +1 -1
  41. package/dist-lib/index.cjs +18 -13
  42. package/dist-lib/jump-to-source-babel.cjs +1 -1
  43. package/dist-lib/jump-to-source-native.cjs +1 -1
  44. package/dist-lib/menu.cjs +1 -1
  45. package/dist-lib/menu.mjs +1 -1
  46. package/dist-lib/metro-fingerprint-registry.cjs +1 -1
  47. package/dist-lib/metro-fingerprint-registry.mjs +1 -1
  48. package/dist-lib/metro-production-bundle.cjs +1 -1
  49. package/dist-lib/metro-production-bundle.mjs +1 -1
  50. package/dist-lib/metro.cjs +1 -1
  51. package/dist-lib/profiles.cjs +1 -1
  52. package/dist-lib/public-brand.cjs +1 -1
  53. package/dist-lib/react-native-host-modules.cjs +1 -1
  54. package/dist-lib/react-native-host-modules.mjs +1 -1
  55. package/dist-lib/render-mode.cjs +1 -1
  56. package/dist-lib/scripts/dev-server-scanner.cjs +36 -18
  57. package/dist-lib/sdk.cjs +1 -1
  58. package/dist-lib/sdk.mjs +1 -1
  59. package/dist-lib/skills.cjs +214 -87
  60. package/dist-lib/vite.cjs +1 -1
  61. package/package.json +1 -1
  62. package/scripts/dev-server-scanner.ts +37 -20
  63. package/src/screenshots/frame-compose.ts +71 -11
package/dist-lib/sdk.mjs CHANGED
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.505 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.507 | (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.505 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.507 | (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;
@@ -5889,12 +5889,12 @@ var init_registry = __esm({
5889
5889
  usage: 1,
5890
5890
  importance: 1,
5891
5891
  estimate: 1,
5892
- rationale: "Tabs.Host and Tabs.Screen, FullWindowOverlay, transition progress, and opt-in React freeze behavior remain available. The inspected tab, overlay, and freeze path has no app-facing behavior gap."
5892
+ rationale: "Tabs.Host and Tabs.Screen support system Search, bottom accessories, measured scroll minimization, active-tab restoration, compact main-tab persistence while Search is selected, and tab selection. FullWindowOverlay, transition progress, and opt-in React freeze behavior remain available. The inspected tab, overlay, and freeze path has no app-facing behavior gap."
5893
5893
  }
5894
5894
  ],
5895
5895
  capabilityCoverage: CAPABILITY_INVENTORY_PACKAGE_COVERAGE["react-native-screens"],
5896
- note: "screen container, stack, header config, search bar, navigation props",
5897
- working: "Screen, ScreenContainer, ScreenStack, enableFreeze/freezeEnabled with delayed React freeze, Android modal/pageSheet push fallback, Android statusBarHidden/statusBarStyle/navigationBarHidden traits, push/pop slide, replaceAnimation push/pop direction, zoom/fade/fade_from_bottom transitions, edge swipe-back, parallax behind-screen, large title collapse, header (back button, inline/large title, custom left/center/right/search bar subviews, native header bar buttons/menus/badges, crossfade), formSheet custom initial detents/corner radius/grabber/undimmed range, numeric-detent dragging and settling, eligible content scrolling, detent callbacks, guarded dismissal and stacked sheets, useHeaderHeight, useTransitionProgress, FullWindowOverlay, SearchBar with stacked and iOS 26 automatic/inline/integrated/integratedCentered/integratedButton toolbar placement, Tabs.Host/Screen, lifecycle callbacks (onAppear/onDisappear/onWillAppear/onWillDisappear), onDismissed",
5896
+ note: "screen container, stack, header and search chrome, native tabs, navigation props",
5897
+ working: "Screen, ScreenContainer, ScreenStack, enableFreeze/freezeEnabled with delayed React freeze, Android modal/pageSheet push fallback, Android statusBarHidden/statusBarStyle/navigationBarHidden traits, push/pop slide, replaceAnimation push/pop direction, zoom/fade/fade_from_bottom transitions, edge swipe-back, parallax behind-screen, large title collapse, header (back button, inline/large title, custom left/center/right/search bar subviews, native header bar buttons/menus/badges, crossfade), formSheet custom initial detents/corner radius/grabber/undimmed range, numeric-detent dragging and settling, eligible content scrolling, detent callbacks, guarded dismissal and stacked sheets, useHeaderHeight, useTransitionProgress, FullWindowOverlay, SearchBar with stacked and iOS 26 automatic/inline/integrated/integratedCentered/integratedButton toolbar placement, Tabs.Host/Screen with detached system Search, regular/inline bottomAccessory, tabBarMinimizeBehavior none/automatic/onScrollDown/onScrollUp, active-tab restoration and prior-main-tab persistence while Search is compact, lifecycle callbacks (onAppear/onDisappear/onWillAppear/onWillDisappear), onDismissed",
5898
5898
  missing: "native velocity and spring fidelity, pointer cancellation, fit-to-content and non-numeric detents, numeric 1.0 native geometry, and expandsWhenScrolledToEdge remain incomplete or unverified"
5899
5899
  }
5900
5900
  ]
@@ -9439,9 +9439,9 @@ var init_registry = __esm({
9439
9439
  {
9440
9440
  range: ">=1.21.0",
9441
9441
  coverage: 0.72,
9442
- note: "native-seam only for toolbar/menu host views. The real @vxrn/native React code runs from the bundle and converts its public props into native host props.",
9443
- working: "ToolbarHost, ToolbarItem with SF Symbols/image sources, badges, fixed/fluid spacers, view children, nested MenuAction trees, disabled/selected/destructive actions, and visible action callbacks",
9444
- missing: "Color and zoom-transition native hosts, SplitView gamma host, bottom SearchBarSlot integration with a configured native search controller, exact UIKit shared-background grouping"
9442
+ note: "native-seam for toolbar/menu and zoom-transition host views. The real @vxrn/native React code runs from the bundle and converts its public props into native host props.",
9443
+ working: "ToolbarHost, ToolbarItem with SF Symbols/image sources, badges, fixed/fluid spacers, view children, nested MenuAction trees, disabled/selected/destructive actions, visible action callbacks, ZoomTransitionSource source-window registration, and ZoomTransitionEnabler destination alignment",
9444
+ missing: "Color native host, SplitView gamma host, bottom SearchBarSlot integration with a configured native search controller, exact UIKit shared-background grouping"
9445
9445
  }
9446
9446
  ]
9447
9447
  },
@@ -20007,20 +20007,34 @@ function applyManifest(result, manifestRes, buildIconProxyUrl) {
20007
20007
  if (launchUrl2 && !result.patched && !isDirectOneBundleUrl(result.bundleUrl)) {
20008
20008
  result.bundleUrl = withRuntimeConfig(result.port, launchUrl2, result.cwd);
20009
20009
  }
20010
- const rawIconUrl = client.iconUrl || client.ios?.iconUrl || client.icon || client.ios?.icon;
20011
- if (rawIconUrl) {
20012
- result.iconPath = rawIconUrl;
20013
- if (buildIconProxyUrl) {
20014
- if (rawIconUrl.startsWith("http")) {
20015
- result.iconUrl = buildIconProxyUrl(rawIconUrl);
20010
+ const localIcon = client.icon || client.ios?.icon;
20011
+ const remoteIcon = client.iconUrl || client.ios?.iconUrl;
20012
+ const rawIcon = localIcon || remoteIcon;
20013
+ if (rawIcon) {
20014
+ result.iconPath = rawIcon;
20015
+ let resolvedIconUrl;
20016
+ if (rawIcon.startsWith("http://") || rawIcon.startsWith("https://")) {
20017
+ let isLoopback = false;
20018
+ try {
20019
+ const parsed = new URL(rawIcon);
20020
+ isLoopback = isLoopbackHost(parsed.hostname);
20021
+ } catch {
20022
+ }
20023
+ if (buildIconProxyUrl) {
20024
+ if (isLoopback) {
20025
+ resolvedIconUrl = buildIconProxyUrl(rawIcon);
20026
+ }
20016
20027
  } else {
20017
- const cleanPath = rawIconUrl.replace(/^\.\//, "");
20018
- result.iconUrl = buildIconProxyUrl(
20019
- `${buildLoopbackProbeBaseUrl("http:", manifestRes.hostname, result.port)}/assets/${cleanPath}`
20020
- );
20028
+ resolvedIconUrl = rawIcon;
20021
20029
  }
20022
20030
  } else {
20023
- result.iconUrl = rawIconUrl.startsWith("http") ? rawIconUrl : `${buildLoopbackProbeBaseUrl("http:", manifestRes.hostname, result.port)}/assets/${rawIconUrl.replace(/^\.\//, "")}`;
20031
+ const cleanPath = rawIcon.replace(/^\.?\//, "");
20032
+ const assetPath = cleanPath.startsWith("assets/") ? `/${cleanPath}` : `/assets/${cleanPath}`;
20033
+ const localUrl = `${buildLoopbackProbeBaseUrl("http:", manifestRes.hostname, result.port)}${assetPath}`;
20034
+ resolvedIconUrl = buildIconProxyUrl ? buildIconProxyUrl(localUrl) : localUrl;
20035
+ }
20036
+ if (resolvedIconUrl) {
20037
+ result.iconUrl = resolvedIconUrl;
20024
20038
  }
20025
20039
  }
20026
20040
  } catch {
@@ -24787,6 +24801,9 @@ function modernButtons(action, volumeUp, volumeDown, power) {
24787
24801
  function uniformFrameInsets(value) {
24788
24802
  return { top: value, right: value, bottom: value, left: value };
24789
24803
  }
24804
+ function cornerRadii(value) {
24805
+ return { topLeft: value, topRight: value, bottomRight: value, bottomLeft: value };
24806
+ }
24790
24807
  function iosDevice(spec) {
24791
24808
  return {
24792
24809
  ...spec,
@@ -24808,8 +24825,36 @@ function iosDevice(spec) {
24808
24825
  modelName: spec.name
24809
24826
  };
24810
24827
  }
24811
- function getDevice(model) {
24812
- return devices[model];
24828
+ function resolveDevicePosture(model, posture) {
24829
+ const supported = devices[model].supportedPostures;
24830
+ return posture && supported?.includes(posture) ? posture : supported?.[0] ?? "closed";
24831
+ }
24832
+ function getDevice(model, posture) {
24833
+ const device = devices[model];
24834
+ if (!device.supportedPostures) return device;
24835
+ const resolvedPosture = resolveDevicePosture(model, posture);
24836
+ if (resolvedPosture === "closed") return device;
24837
+ return {
24838
+ ...device,
24839
+ // inner display size from apple's ios 27 ui kit. corners and insets are
24840
+ // measured from apple's 2d inner product art: every display corner fits a
24841
+ // 53pt circle within 0.4px, and the silhouette sits 19.5pt outside the
24842
+ // display on every edge as 16pt of black glass plus a 3.5pt rail.
24843
+ width: 951,
24844
+ height: 669,
24845
+ statusBarHeight: 44,
24846
+ cornerRadius: cornerRadii(53),
24847
+ continuousCornerRadius: cornerRadii(53),
24848
+ frameInsets: uniformFrameInsets(19.5),
24849
+ housing: { rail: uniformFrameInsets(3.5), hingeBarrel: null },
24850
+ cameraCutout: null,
24851
+ physicalPPI: 430,
24852
+ posture: "open",
24853
+ // open inner display keeps horizontal bars in this slice; preserve the
24854
+ // previous trailing status lane so open layouts do not pick up the closed
24855
+ // 84pt Vertical Bar inset.
24856
+ safeAreaInsets: { top: 0, bottom: 0, left: 0, right: 72 }
24857
+ };
24813
24858
  }
24814
24859
  function listDeviceModels() {
24815
24860
  return Object.keys(devices);
@@ -24858,8 +24903,8 @@ var init_devices = __esm({
24858
24903
  scale: 1,
24859
24904
  statusBarHeight: 0,
24860
24905
  homeIndicatorHeight: 0,
24861
- cornerRadius: 0,
24862
- continuousCornerRadius: 0,
24906
+ cornerRadius: cornerRadii(0),
24907
+ continuousCornerRadius: cornerRadii(0),
24863
24908
  frameInsets: uniformFrameInsets(16),
24864
24909
  dynamicIsland: null,
24865
24910
  safeAreaInsets: { top: 0, bottom: 0, left: 0, right: 0 },
@@ -24883,8 +24928,8 @@ var init_devices = __esm({
24883
24928
  scale: 2,
24884
24929
  statusBarHeight: 20,
24885
24930
  homeIndicatorHeight: 0,
24886
- cornerRadius: 0,
24887
- continuousCornerRadius: 0,
24931
+ cornerRadius: cornerRadii(0),
24932
+ continuousCornerRadius: cornerRadii(0),
24888
24933
  // legacy home-button frame uses asymmetric LEGACY_*_BEZEL constants in the
24889
24934
  // shell (111pt forehead/chin, 29pt sides); uniform frameInsets are a sane
24890
24935
  // modern-path value and mirror the SE's ~28.7pt side bezel.
@@ -24903,8 +24948,8 @@ var init_devices = __esm({
24903
24948
  scale: 3,
24904
24949
  statusBarHeight: 47,
24905
24950
  homeIndicatorHeight: 34,
24906
- cornerRadius: 46.2,
24907
- continuousCornerRadius: 67.462,
24951
+ cornerRadius: cornerRadii(46.2),
24952
+ continuousCornerRadius: cornerRadii(67.462),
24908
24953
  frameInsets: uniformFrameInsets(19.5),
24909
24954
  dynamicIsland: null,
24910
24955
  safeAreaInsets: { top: 47, bottom: 34, left: 0, right: 0 },
@@ -24925,8 +24970,8 @@ var init_devices = __esm({
24925
24970
  scale: 3,
24926
24971
  statusBarHeight: 59,
24927
24972
  homeIndicatorHeight: 34,
24928
- cornerRadius: 53.9,
24929
- continuousCornerRadius: 73.402,
24973
+ cornerRadius: cornerRadii(53.9),
24974
+ continuousCornerRadius: cornerRadii(73.402),
24930
24975
  frameInsets: uniformFrameInsets(18.2),
24931
24976
  dynamicIsland: { width: 126, height: 37.3, top: 14.1 },
24932
24977
  safeAreaInsets: { top: 59, bottom: 34, left: 0, right: 0 },
@@ -24947,8 +24992,8 @@ var init_devices = __esm({
24947
24992
  scale: 3,
24948
24993
  statusBarHeight: 59,
24949
24994
  homeIndicatorHeight: 34,
24950
- cornerRadius: 53.9,
24951
- continuousCornerRadius: 74.654,
24995
+ cornerRadius: cornerRadii(53.9),
24996
+ continuousCornerRadius: cornerRadii(74.654),
24952
24997
  frameInsets: uniformFrameInsets(17.3),
24953
24998
  dynamicIsland: { width: 126, height: 37.3, top: 11.4 },
24954
24999
  safeAreaInsets: { top: 59, bottom: 34, left: 0, right: 0 },
@@ -24969,8 +25014,8 @@ var init_devices = __esm({
24969
25014
  scale: 3,
24970
25015
  statusBarHeight: 59,
24971
25016
  homeIndicatorHeight: 34,
24972
- cornerRadius: 53.9,
24973
- continuousCornerRadius: 73.402,
25017
+ cornerRadius: cornerRadii(53.9),
25018
+ continuousCornerRadius: cornerRadii(73.402),
24974
25019
  frameInsets: uniformFrameInsets(15.3),
24975
25020
  dynamicIsland: { width: 126, height: 38, top: 13.4 },
24976
25021
  safeAreaInsets: { top: 59, bottom: 34, left: 0, right: 0 },
@@ -24991,8 +25036,8 @@ var init_devices = __esm({
24991
25036
  scale: 3,
24992
25037
  statusBarHeight: 59,
24993
25038
  homeIndicatorHeight: 34,
24994
- cornerRadius: 53.9,
24995
- continuousCornerRadius: 74.654,
25039
+ cornerRadius: cornerRadii(53.9),
25040
+ continuousCornerRadius: cornerRadii(74.654),
24996
25041
  frameInsets: uniformFrameInsets(15.3),
24997
25042
  dynamicIsland: { width: 126, height: 38, top: 13.3 },
24998
25043
  safeAreaInsets: { top: 59, bottom: 34, left: 0, right: 0 },
@@ -25017,8 +25062,8 @@ var init_devices = __esm({
25017
25062
  // mask (extracted from the simdevicetype bundle PDF; see
25018
25063
  // packages/sootsim-engine/assets/device-frame-geometry). radius is a paint clip only — the
25019
25064
  // calibrated conformance-oracle layout fields below are unchanged.
25020
- cornerRadius: 53.9,
25021
- continuousCornerRadius: 73.402,
25065
+ cornerRadius: cornerRadii(53.9),
25066
+ continuousCornerRadius: cornerRadii(73.402),
25022
25067
  frameInsets: uniformFrameInsets(15.3),
25023
25068
  // island top kept at 11 (real 11.3) — this is the calibrated conformance /
25024
25069
  // proof oracle profile; the status-bar clock offsets are tuned against it.
@@ -25041,8 +25086,8 @@ var init_devices = __esm({
25041
25086
  scale: 3,
25042
25087
  statusBarHeight: 59,
25043
25088
  homeIndicatorHeight: 34,
25044
- cornerRadius: 53.9,
25045
- continuousCornerRadius: 74.654,
25089
+ cornerRadius: cornerRadii(53.9),
25090
+ continuousCornerRadius: cornerRadii(74.654),
25046
25091
  frameInsets: uniformFrameInsets(15.3),
25047
25092
  dynamicIsland: { width: 126, height: 37.3, top: 11.4 },
25048
25093
  safeAreaInsets: { top: 59, bottom: 34, left: 0, right: 0 },
@@ -25065,8 +25110,8 @@ var init_devices = __esm({
25065
25110
  // app content at y=186px, i.e. 62 logical pt at 3x.
25066
25111
  statusBarHeight: 62,
25067
25112
  homeIndicatorHeight: 34,
25068
- cornerRadius: 61,
25069
- continuousCornerRadius: 84.661,
25113
+ cornerRadius: cornerRadii(61),
25114
+ continuousCornerRadius: cornerRadii(84.661),
25070
25115
  frameInsets: uniformFrameInsets(15.3),
25071
25116
  dynamicIsland: { width: 126, height: 38, top: 13.5 },
25072
25117
  safeAreaInsets: { top: 62, bottom: 34, left: 0, right: 0 },
@@ -25090,8 +25135,8 @@ var init_devices = __esm({
25090
25135
  // remains at the 59pt profile.
25091
25136
  statusBarHeight: 62,
25092
25137
  homeIndicatorHeight: 34,
25093
- cornerRadius: 61.3,
25094
- continuousCornerRadius: 85.163,
25138
+ cornerRadius: cornerRadii(61.3),
25139
+ continuousCornerRadius: cornerRadii(85.163),
25095
25140
  frameInsets: uniformFrameInsets(15.4),
25096
25141
  dynamicIsland: { width: 126, height: 38, top: 14 },
25097
25142
  safeAreaInsets: { top: 62, bottom: 34, left: 0, right: 0 },
@@ -25112,8 +25157,8 @@ var init_devices = __esm({
25112
25157
  scale: 3,
25113
25158
  statusBarHeight: 59,
25114
25159
  homeIndicatorHeight: 34,
25115
- cornerRadius: 61,
25116
- continuousCornerRadius: 84.661,
25160
+ cornerRadius: cornerRadii(61),
25161
+ continuousCornerRadius: cornerRadii(84.661),
25117
25162
  // display border 1.44mm plus the visible flat housing rail.
25118
25163
  frameInsets: uniformFrameInsets(15.3),
25119
25164
  dynamicIsland: { width: 126, height: 37.3, top: 14 },
@@ -25138,8 +25183,8 @@ var init_devices = __esm({
25138
25183
  scale: 3,
25139
25184
  statusBarHeight: 59,
25140
25185
  homeIndicatorHeight: 34,
25141
- cornerRadius: 64.6,
25142
- continuousCornerRadius: 88.313,
25186
+ cornerRadius: cornerRadii(64.6),
25187
+ continuousCornerRadius: cornerRadii(88.313),
25143
25188
  // WHICH QUANTITY THIS IS, because it has been flipped twice and each pass
25144
25189
  // read the other one as a bug: frameInsets is the VISIBLE FACE-ON frame —
25145
25190
  // the black display border plus however much housing rail you actually see
@@ -25175,8 +25220,8 @@ var init_devices = __esm({
25175
25220
  scale: 3,
25176
25221
  statusBarHeight: 59,
25177
25222
  homeIndicatorHeight: 34,
25178
- cornerRadius: 61,
25179
- continuousCornerRadius: 84.661,
25223
+ cornerRadius: cornerRadii(61),
25224
+ continuousCornerRadius: cornerRadii(84.661),
25180
25225
  // display border 1.44mm plus the visible flat housing rail.
25181
25226
  frameInsets: uniformFrameInsets(15.3),
25182
25227
  dynamicIsland: { width: 126, height: 38, top: 14 },
@@ -25200,8 +25245,8 @@ var init_devices = __esm({
25200
25245
  // iphone 16 pro max screens proof oracle.
25201
25246
  statusBarHeight: 62,
25202
25247
  homeIndicatorHeight: 34,
25203
- cornerRadius: 61.3,
25204
- continuousCornerRadius: 85.163,
25248
+ cornerRadius: cornerRadii(61.3),
25249
+ continuousCornerRadius: cornerRadii(85.163),
25205
25250
  // display border 1.36mm plus the visible flat housing rail.
25206
25251
  frameInsets: uniformFrameInsets(15.4),
25207
25252
  dynamicIsland: { width: 126, height: 38, top: 14 },
@@ -25216,6 +25261,47 @@ var init_devices = __esm({
25216
25261
  [292.2, 98.3]
25217
25262
  )
25218
25263
  }),
25264
+ "iphone-duo": {
25265
+ ...iosDevice({
25266
+ name: "iPhone Duo",
25267
+ width: 466,
25268
+ height: 678,
25269
+ scale: 3,
25270
+ statusBarHeight: 66,
25271
+ homeIndicatorHeight: 0,
25272
+ // hinge on the left. the hinge-side display corners measure 5pt in
25273
+ // apple's 2d outer product art; the free side keeps the 61pt pro curve.
25274
+ cornerRadius: { topLeft: 5, topRight: 61, bottomRight: 61, bottomLeft: 5 },
25275
+ continuousCornerRadius: {
25276
+ topLeft: 5,
25277
+ topRight: 61,
25278
+ bottomRight: 61,
25279
+ bottomLeft: 5
25280
+ },
25281
+ // display edge to silhouette. left is black glass 8.6 + rail 5.4 + hinge
25282
+ // barrel 14.1; right is black glass 8.7 + rail 7.7.
25283
+ frameInsets: { top: 19, right: 16.4, bottom: 16.3, left: 28.1 },
25284
+ housing: {
25285
+ rail: { top: 10.4, right: 7.7, bottom: 7.7, left: 19.5 },
25286
+ hingeBarrel: { width: 14.1, endInset: 6.4 }
25287
+ },
25288
+ dynamicIsland: null,
25289
+ // 84pt trailing Vertical Bar on the outer display (Apple iOS 27 UI kit).
25290
+ safeAreaInsets: { top: 0, bottom: 0, left: 0, right: 84 },
25291
+ iosVersion: "27.1",
25292
+ keyboardHeight: 0,
25293
+ physicalPPI: 460,
25294
+ // official model extraction for button positions is outside this slice.
25295
+ hardwareButtons: modernButtons([0, 0], [0, 0], [0, 0], [0, 0]),
25296
+ posture: "closed",
25297
+ supportedPostures: ["closed", "open"]
25298
+ }),
25299
+ cameraCutout: {
25300
+ diameter: 37,
25301
+ top: 29,
25302
+ trailing: 29
25303
+ }
25304
+ },
25219
25305
  "pixel-8": {
25220
25306
  platform: "android",
25221
25307
  name: "Pixel 8",
@@ -25235,8 +25321,8 @@ var init_devices = __esm({
25235
25321
  left: 0,
25236
25322
  right: 0
25237
25323
  },
25238
- cornerRadius: 39,
25239
- continuousCornerRadius: 39,
25324
+ cornerRadius: cornerRadii(39),
25325
+ continuousCornerRadius: cornerRadii(39),
25240
25326
  // android studio's pixel 8 device art places the 1080x2400 display at
25241
25327
  // (49,55) in a 1187x2513 body. the right side button occupies the final
25242
25328
  // 8 physical pixels, so the body insets are 49/50/58/55 at 2.625x.
@@ -25283,8 +25369,8 @@ var init_devices = __esm({
25283
25369
  left: 0,
25284
25370
  right: 0
25285
25371
  },
25286
- cornerRadius: 39,
25287
- continuousCornerRadius: 39,
25372
+ cornerRadius: cornerRadii(39),
25373
+ continuousCornerRadius: cornerRadii(39),
25288
25374
  frameInsets: {
25289
25375
  top: 55 / 2.625,
25290
25376
  right: 50 / 2.625,
@@ -25323,8 +25409,8 @@ var init_devices = __esm({
25323
25409
  keyboardHeight: 308,
25324
25410
  navigationBarMode: "gesture",
25325
25411
  displayCutout: { top: 40, bottom: 0, left: 0, right: 0 },
25326
- cornerRadius: 41,
25327
- continuousCornerRadius: 41,
25412
+ cornerRadius: cornerRadii(41),
25413
+ continuousCornerRadius: cornerRadii(41),
25328
25414
  // Pixel 9 Pro XL body 162.8×76.6mm over a 70.2mm panel → ~1.8mm ≈ 12pt.
25329
25415
  frameInsets: uniformFrameInsets(12),
25330
25416
  dynamicIsland: null,
@@ -25359,8 +25445,8 @@ var init_devices = __esm({
25359
25445
  keyboardHeight: 308,
25360
25446
  navigationBarMode: "gesture",
25361
25447
  displayCutout: { top: 40, bottom: 0, left: 0, right: 0 },
25362
- cornerRadius: 40,
25363
- continuousCornerRadius: 40,
25448
+ cornerRadius: cornerRadii(40),
25449
+ continuousCornerRadius: cornerRadii(40),
25364
25450
  // the 72mm body surrounds a roughly 65.1mm-wide 6.3-inch panel, which
25365
25451
  // resolves to about 18 logical points of frame around the 1080px display.
25366
25452
  frameInsets: uniformFrameInsets(18),
@@ -25396,8 +25482,8 @@ var init_devices = __esm({
25396
25482
  keyboardHeight: 308,
25397
25483
  navigationBarMode: "gesture",
25398
25484
  displayCutout: { top: 36, bottom: 0, left: 0, right: 0 },
25399
- cornerRadius: 36,
25400
- continuousCornerRadius: 36,
25485
+ cornerRadius: cornerRadii(36),
25486
+ continuousCornerRadius: cornerRadii(36),
25401
25487
  // Galaxy S24 body 147.0×70.6mm over a 65.9mm panel → ~1.2mm ≈ 8pt (Samsung
25402
25488
  // flat-display bezels are noticeably thinner than Pixel's).
25403
25489
  frameInsets: uniformFrameInsets(8),
@@ -25434,8 +25520,8 @@ var init_devices = __esm({
25434
25520
  keyboardHeight: 308,
25435
25521
  navigationBarMode: "gesture",
25436
25522
  displayCutout: { top: 36, bottom: 0, left: 0, right: 0 },
25437
- cornerRadius: 24,
25438
- continuousCornerRadius: 24,
25523
+ cornerRadius: cornerRadii(24),
25524
+ continuousCornerRadius: cornerRadii(24),
25439
25525
  // Galaxy S24 Ultra body 162.3×79.0mm over a 72.4mm panel → ~1.5mm ≈ 9pt.
25440
25526
  frameInsets: uniformFrameInsets(9),
25441
25527
  dynamicIsland: null,
@@ -31152,19 +31238,21 @@ function createBridge(wsPort, opts = {}) {
31152
31238
  });
31153
31239
  let warnedAboutContention = false;
31154
31240
  let noticedTarget = false;
31155
- const hostPidBySimId = /* @__PURE__ */ new Map();
31241
+ const lineageBySimId = /* @__PURE__ */ new Map();
31156
31242
  const lineageLookupBySimId = /* @__PURE__ */ new Map();
31157
31243
  function pickReloadedTarget(sims, retiredSimId) {
31158
- const pinnedHostPid = hostPidBySimId.get(retiredSimId);
31159
- if (pinnedHostPid === void 0) return null;
31244
+ const lineage = lineageBySimId.get(retiredSimId);
31245
+ if (!lineage) return null;
31160
31246
  const matches = (Array.isArray(sims) ? sims : []).filter(
31161
- (sim) => sim.readyState === "open" && sim.meta?.sootsimHostPid === pinnedHostPid
31247
+ (sim) => sim.readyState === "open" && sim.meta?.sootsimHostPid === lineage.hostPid && sim.connectedAt > lineage.connectedAt
31162
31248
  );
31163
- const restored = matches.find((sim) => sim.id === retiredSimId);
31164
- if (restored) return restored.id;
31165
- if (matches.length !== 1) return null;
31166
- hostPidBySimId.set(matches[0].id, pinnedHostPid);
31167
- return matches[0].id;
31249
+ const successor = matches.find((sim) => sim.id === retiredSimId) ?? (matches.length === 1 ? matches[0] : null);
31250
+ if (!successor) return null;
31251
+ lineageBySimId.set(successor.id, {
31252
+ hostPid: lineage.hostPid,
31253
+ connectedAt: successor.connectedAt
31254
+ });
31255
+ return successor.id;
31168
31256
  }
31169
31257
  function maybeNoticeTarget(cmdType, simId) {
31170
31258
  if (noticedTarget) return;
@@ -31254,7 +31342,7 @@ function createBridge(wsPort, opts = {}) {
31254
31342
  const sentSimId = opts.simIdSource === "flag" ? opts.simId : readCurrentSimId() ?? opts.simId;
31255
31343
  const targetedSimId = typeof cmd.simId === "string" && cmd.simId ? cmd.simId : sentSimId;
31256
31344
  try {
31257
- if (targetedSimId && cmd.type !== "bridge:list-sims" && !hostPidBySimId.has(targetedSimId)) {
31345
+ if (targetedSimId && cmd.type !== "bridge:list-sims" && !lineageBySimId.has(targetedSimId)) {
31258
31346
  let lookup = lineageLookupBySimId.get(targetedSimId);
31259
31347
  if (!lookup) {
31260
31348
  lookup = (async () => {
@@ -31265,8 +31353,11 @@ function createBridge(wsPort, opts = {}) {
31265
31353
  (sim) => sim.id === targetedSimId
31266
31354
  );
31267
31355
  const hostPid = targeted?.meta?.sootsimHostPid;
31268
- if (typeof hostPid === "number") {
31269
- hostPidBySimId.set(targetedSimId, hostPid);
31356
+ if (targeted && typeof hostPid === "number") {
31357
+ lineageBySimId.set(targetedSimId, {
31358
+ hostPid,
31359
+ connectedAt: targeted.connectedAt
31360
+ });
31270
31361
  }
31271
31362
  } catch {
31272
31363
  } finally {
@@ -35056,7 +35147,7 @@ async function runOpenCommand(args, opts = {}) {
35056
35147
  quiet
35057
35148
  );
35058
35149
  await maybeDescribeAfterOpen(parsed.wsPort, targetSim.id, args);
35059
- return parsed.wsPort;
35150
+ return { simId: targetSim.id, launched: false };
35060
35151
  }
35061
35152
  } finally {
35062
35153
  bridge.close();
@@ -35130,7 +35221,7 @@ async function runOpenCommand(args, opts = {}) {
35130
35221
  signalDriverLaunchConnected(result);
35131
35222
  printOpenedSim(openUrl2, match, `${driver.name} driver`, quiet);
35132
35223
  await maybeDescribeAfterOpen(parsed.wsPort, match.id, args);
35133
- return parsed.wsPort;
35224
+ return { simId: match.id, launched: true };
35134
35225
  }
35135
35226
  async function runUseLikeCommand(args, opts = {}) {
35136
35227
  const parsed = parseBridgeCliArgs(args, {
@@ -35571,10 +35662,12 @@ var require_proof_frame = __commonJS({
35571
35662
  function getFramedScreenshotLayout(model) {
35572
35663
  const spec = (0, import_settings2.getDevice)(model);
35573
35664
  const renderScale = spec.scale;
35574
- const isLegacyHardware = spec.statusBarHeight > 0 && !spec.dynamicIsland && spec.homeIndicatorHeight === 0 && spec.cornerRadius === 0;
35665
+ const isLegacyHardware = spec.statusBarHeight > 0 && !spec.dynamicIsland && spec.homeIndicatorHeight === 0 && // home-button iphones have a square display, so any corner reads 0
35666
+ spec.cornerRadius.topLeft === 0;
35575
35667
  const logicalScreenWidth = spec.width;
35576
35668
  const logicalScreenHeight = spec.height;
35577
35669
  const modernGeometry = (0, import_settings2.resolveDeviceFrameGeometry)(spec);
35670
+ const housing = isLegacyHardware ? null : modernGeometry.housing;
35578
35671
  const logicalFrameWidth = isLegacyHardware ? spec.width + import_chrome_metrics.LEGACY_SIDE_BEZEL * 2 : modernGeometry.frameWidth;
35579
35672
  const logicalFrameHeight = isLegacyHardware ? spec.height + import_chrome_metrics.LEGACY_TOP_BEZEL + import_chrome_metrics.LEGACY_BOTTOM_BEZEL : modernGeometry.frameHeight;
35580
35673
  const logicalFrameLeft = import_chrome_metrics.SIDE_BUTTON_RESERVE;
@@ -35583,7 +35676,7 @@ function getFramedScreenshotLayout(model) {
35583
35676
  const logicalOuterHeight = logicalFrameHeight + FRAME_OUTLINE_BLEED * 2;
35584
35677
  const logicalScreenLeft = logicalFrameLeft + (isLegacyHardware ? import_chrome_metrics.LEGACY_SIDE_BEZEL : modernGeometry.screenLeft);
35585
35678
  const logicalScreenTop = isLegacyHardware ? import_chrome_metrics.LEGACY_TOP_BEZEL : modernGeometry.screenTop;
35586
- const logicalFrameRadius = isLegacyHardware ? import_chrome_metrics.LEGACY_FRAME_RADIUS : modernGeometry.frameRadius;
35679
+ const logicalFrameRadius = isLegacyHardware ? (0, import_settings2.cornerRadii)(import_chrome_metrics.LEGACY_FRAME_RADIUS) : modernGeometry.frameRadius;
35587
35680
  const buttons = [
35588
35681
  {
35589
35682
  side: spec.hardwareButtons.lock.side,
@@ -35625,11 +35718,12 @@ function getFramedScreenshotLayout(model) {
35625
35718
  logicalScreenHeight,
35626
35719
  logicalScreenLeft,
35627
35720
  logicalScreenTop,
35628
- logicalScreenRadius: isLegacyHardware ? 0 : modernGeometry.screenRadius,
35721
+ logicalScreenRadius: isLegacyHardware ? (0, import_settings2.cornerRadii)(0) : modernGeometry.screenRadius,
35629
35722
  logicalFrameRadius,
35630
- frameBackground: isLegacyHardware ? "linear-gradient(180deg, #1b1c20 0%, #0f1012 42%, #050608 100%)" : "#000000",
35631
- frameOutline: isLegacyHardware ? "0 0 0 1px #1f2125" : "0 0 0 1px #333",
35632
- metallicRingShadow: isLegacyHardware ? null : METALLIC_RING_SHADOW,
35723
+ housing,
35724
+ frameBackground: isLegacyHardware ? "linear-gradient(180deg, #1b1c20 0%, #0f1012 42%, #050608 100%)" : housing ? import_chrome_metrics.HOUSING_RAIL_COLOR : "#000000",
35725
+ frameOutline: isLegacyHardware ? "0 0 0 1px #1f2125" : housing ? import_chrome_metrics.HOUSING_RAIL_OUTLINE : "0 0 0 1px #333",
35726
+ metallicRingShadow: isLegacyHardware || housing ? null : METALLIC_RING_SHADOW,
35633
35727
  buttons,
35634
35728
  legacyHomeButton: isLegacyHardware ? {
35635
35729
  top: import_chrome_metrics.LEGACY_TOP_BEZEL + spec.height + (import_chrome_metrics.LEGACY_BOTTOM_BEZEL - import_chrome_metrics.LEGACY_HOME_BUTTON_SIZE) / 2,
@@ -35679,13 +35773,45 @@ function renderFrameHtml(layout, rawImageDataUrl) {
35679
35773
  style="
35680
35774
  position:absolute;
35681
35775
  inset:0;
35682
- border-radius:${layout.logicalFrameRadius}px;
35776
+ border-radius:${(0, import_settings2.formatCornerRadii)(layout.logicalFrameRadius)};
35683
35777
  box-shadow:${layout.metallicRingShadow};
35684
35778
  pointer-events:none;
35685
35779
  z-index:10;
35686
35780
  "
35687
35781
  ></div>
35688
35782
  ` : "";
35783
+ const housingHtml = layout.housing ? `
35784
+ <div
35785
+ aria-hidden="true"
35786
+ style="
35787
+ position:absolute;
35788
+ top:${layout.housing.rail.top}px;
35789
+ right:${layout.housing.rail.right}px;
35790
+ bottom:${layout.housing.rail.bottom}px;
35791
+ left:${layout.housing.rail.left}px;
35792
+ border-radius:${(0, import_settings2.formatCornerRadii)(layout.housing.glassRadius)};
35793
+ background:#000000;
35794
+ pointer-events:none;
35795
+ "
35796
+ ></div>
35797
+ ${layout.housing.hingeBarrel ? `
35798
+ <div
35799
+ aria-hidden="true"
35800
+ style="
35801
+ position:absolute;
35802
+ top:${layout.housing.hingeBarrel.endInset}px;
35803
+ bottom:${layout.housing.hingeBarrel.endInset}px;
35804
+ left:0;
35805
+ width:${layout.housing.hingeBarrel.width}px;
35806
+ border-radius:${layout.housing.hingeBarrel.width / 2}px 0 0 ${layout.housing.hingeBarrel.width / 2}px;
35807
+ background:${import_chrome_metrics.HINGE_BARREL_BACKGROUND};
35808
+ box-shadow:${import_chrome_metrics.HINGE_BARREL_SHADOW};
35809
+ pointer-events:none;
35810
+ z-index:11;
35811
+ "
35812
+ ></div>
35813
+ ` : ""}
35814
+ ` : "";
35689
35815
  const legacyHomeButtonHtml = layout.legacyHomeButton ? `
35690
35816
  <div
35691
35817
  aria-hidden="true"
@@ -35799,7 +35925,7 @@ function renderFrameHtml(layout, rawImageDataUrl) {
35799
35925
  left:${layout.logicalFrameLeft}px;
35800
35926
  width:${layout.logicalFrameWidth}px;
35801
35927
  height:${layout.logicalFrameHeight}px;
35802
- border-radius:${layout.logicalFrameRadius}px;
35928
+ border-radius:${(0, import_settings2.formatCornerRadii)(layout.logicalFrameRadius)};
35803
35929
  box-sizing:border-box;
35804
35930
  overflow:visible;
35805
35931
  background:${layout.frameBackground};
@@ -35808,6 +35934,7 @@ function renderFrameHtml(layout, rawImageDataUrl) {
35808
35934
  >
35809
35935
  ${buttonsHtml}
35810
35936
  ${ringHtml}
35937
+ ${housingHtml}
35811
35938
  ${legacyHomeButtonHtml}
35812
35939
  <div
35813
35940
  id="screen"
@@ -35817,7 +35944,7 @@ function renderFrameHtml(layout, rawImageDataUrl) {
35817
35944
  left:${layout.logicalScreenLeft - layout.logicalFrameLeft}px;
35818
35945
  width:${layout.logicalScreenWidth}px;
35819
35946
  height:${layout.logicalScreenHeight}px;
35820
- border-radius:${layout.logicalScreenRadius}px;
35947
+ border-radius:${(0, import_settings2.formatCornerRadii)(layout.logicalScreenRadius)};
35821
35948
  overflow:hidden;
35822
35949
  background:#000;
35823
35950
  "
@@ -37239,7 +37366,7 @@ var init_bridge_flow_runner = __esm({
37239
37366
  });
37240
37367
  const deadline = Date.now() + 3e3;
37241
37368
  while (Date.now() < deadline) {
37242
- const state = await this.evaluate(`(() => {
37369
+ const state = await this.evaluate(`(async () => {
37243
37370
  const keyboard =
37244
37371
  window.__sootsimKeyboard ??
37245
37372
  window.SootSim?.bridges?.keyboard ??
@@ -37247,7 +37374,7 @@ var init_bridge_flow_runner = __esm({
37247
37374
  const test = window.__sootsimTest
37248
37375
  return {
37249
37376
  visible: !!keyboard?.isVisible?.(),
37250
- focused: !!test?.isTextInputFocused?.(),
37377
+ focused: !!(await test?.isTextInputFocused?.()),
37251
37378
  }
37252
37379
  })()`);
37253
37380
  if (!state?.visible && !state?.focused) {
package/dist-lib/vite.cjs CHANGED
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.505 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.507 | (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.505",
3
+ "version": "0.1.507",
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",