rnxsim 0.1.519 → 0.1.520

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 (68) hide show
  1. package/cli/cloud-client.ts +419 -287
  2. package/cli/cloud-dispatch.ts +2 -21
  3. package/cli/cloud-session.ts +31 -85
  4. package/cli/commands/box.ts +15 -11
  5. package/cli/commands/inspect.ts +34 -1
  6. package/cli/commands/platform.ts +184 -232
  7. package/cli/main.ts +10 -2
  8. package/cli/outbound-endpoints.ts +21 -6
  9. package/dist-lib/agent-daemon-client.cjs +1 -1
  10. package/dist-lib/agent-events.cjs +1 -1
  11. package/dist-lib/agent-identity.cjs +1 -1
  12. package/dist-lib/agent-sessions.cjs +1 -1
  13. package/dist-lib/attached-projects.cjs +1 -1
  14. package/dist-lib/auth/shared-session.cjs +1 -1
  15. package/dist-lib/backend-origin.cjs +1 -1
  16. package/dist-lib/beta.cjs +1 -1
  17. package/dist-lib/beta.mjs +1 -1
  18. package/dist-lib/bridge-constants.cjs +1 -1
  19. package/dist-lib/bridge-contract-input.cjs +1 -1
  20. package/dist-lib/bridge-contract-input.mjs +1 -1
  21. package/dist-lib/bridge-contract.cjs +1 -1
  22. package/dist-lib/bridge-contract.mjs +1 -1
  23. package/dist-lib/capture-contract.cjs +1 -1
  24. package/dist-lib/capture-contract.mjs +1 -1
  25. package/dist-lib/cli-constants.cjs +1 -1
  26. package/dist-lib/cloud-contract.cjs +1 -1
  27. package/dist-lib/cloud-contract.mjs +1 -1
  28. package/dist-lib/cloud.cjs +1 -1
  29. package/dist-lib/cloud.mjs +1 -1
  30. package/dist-lib/config.cjs +1 -1
  31. package/dist-lib/detox/index.cjs +1 -1
  32. package/dist-lib/dev-bundle-resolution.cjs +1 -1
  33. package/dist-lib/home-paths.cjs +1 -1
  34. package/dist-lib/host/bridge-host.cjs +112 -39
  35. package/dist-lib/host/fetch-proxy-handler.cjs +1 -1
  36. package/dist-lib/host/fetch-proxy-overrides.cjs +1 -1
  37. package/dist-lib/host/fetch-proxy-overrides.mjs +1 -1
  38. package/dist-lib/host/replacement-module-handler.cjs +1 -1
  39. package/dist-lib/host/websocket-proxy.cjs +1 -1
  40. package/dist-lib/index.cjs +43 -117
  41. package/dist-lib/jump-to-source-babel.cjs +1 -1
  42. package/dist-lib/jump-to-source-native.cjs +1 -1
  43. package/dist-lib/menu.cjs +30 -1
  44. package/dist-lib/menu.mjs +27 -1
  45. package/dist-lib/metro-fingerprint-registry.cjs +1 -1
  46. package/dist-lib/metro-fingerprint-registry.mjs +1 -1
  47. package/dist-lib/metro-production-bundle.cjs +1 -1
  48. package/dist-lib/metro-production-bundle.mjs +1 -1
  49. package/dist-lib/metro.cjs +1 -1
  50. package/dist-lib/profiles.cjs +1 -1
  51. package/dist-lib/public-brand.cjs +1 -1
  52. package/dist-lib/react-native-host-modules.cjs +1 -1
  53. package/dist-lib/react-native-host-modules.mjs +1 -1
  54. package/dist-lib/render-mode.cjs +1 -1
  55. package/dist-lib/scripts/dev-server-scanner.cjs +41 -4
  56. package/dist-lib/sdk.cjs +1 -1
  57. package/dist-lib/sdk.mjs +1 -1
  58. package/dist-lib/skills.cjs +138 -164
  59. package/dist-lib/swift.cjs +674 -12
  60. package/dist-lib/vite.cjs +26 -3
  61. package/package.json +1 -1
  62. package/scripts/dev-server-scanner.ts +72 -5
  63. package/src/cloud-contract.ts +7 -1
  64. package/src/cloud.ts +11 -4
  65. package/src/connect.ts +1 -1
  66. package/src/menu.ts +44 -0
  67. package/src/vite-plugin-one.ts +12 -0
  68. package/src/vite-plugin-swift.ts +17 -5
@@ -1,9 +1,11 @@
1
- /*! rnx v0.1.519 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.520 | (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
+ var __create = Object.create;
4
5
  var __defProp = Object.defineProperty;
5
6
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
6
7
  var __getOwnPropNames = Object.getOwnPropertyNames;
8
+ var __getProtoOf = Object.getPrototypeOf;
7
9
  var __hasOwnProp = Object.prototype.hasOwnProperty;
8
10
  var __export = (target, all) => {
9
11
  for (var name in all)
@@ -17,6 +19,14 @@ var __copyProps = (to, from, except, desc) => {
17
19
  }
18
20
  return to;
19
21
  };
22
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
23
+ // If the importer is in node compatibility mode or this is not an ESM
24
+ // file that has been converted to a CommonJS file using a Babel-
25
+ // compatible transform (i.e. "__esModule" has not been set), then set
26
+ // "default" to the CommonJS "module.exports" for node compatibility.
27
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
28
+ mod
29
+ ));
20
30
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
21
31
 
22
32
  // src/swift.ts
@@ -27,9 +37,13 @@ __export(swift_exports, {
27
37
  module.exports = __toCommonJS(swift_exports);
28
38
 
29
39
  // ../sootsim-swift/src/mount.tsx
40
+ var import_native = require("@react-navigation/native");
41
+ var import_native_stack = require("@react-navigation/native-stack");
42
+ var import_runtime = require("contrast-kit/runtime");
30
43
  var import_ui = require("contrast-kit/ui");
31
44
  var import_react = require("react");
32
45
  var import_react_native = require("react-native");
46
+ var import_react_native_bottom_tabs = __toESM(require("react-native-bottom-tabs"), 1);
33
47
  var import_jsx_runtime = require("react/jsx-runtime");
34
48
  function isRecord(value) {
35
49
  return typeof value === "object" && value !== null;
@@ -78,6 +92,7 @@ function readTree(text) {
78
92
  return value.map(readNode);
79
93
  }
80
94
  var WASI_BADF = 8;
95
+ var WASI_INVAL = 28;
81
96
  function wasiImports(host) {
82
97
  return {
83
98
  args_get: () => 0,
@@ -94,10 +109,18 @@ function wasiImports(host) {
94
109
  },
95
110
  fd_close: () => WASI_BADF,
96
111
  fd_fdstat_get: () => WASI_BADF,
112
+ fd_fdstat_set_flags: () => WASI_BADF,
113
+ fd_filestat_get: () => WASI_BADF,
114
+ fd_filestat_set_size: () => WASI_BADF,
115
+ fd_filestat_set_times: () => WASI_BADF,
116
+ fd_pread: () => WASI_BADF,
97
117
  fd_prestat_get: () => WASI_BADF,
98
118
  fd_prestat_dir_name: () => WASI_BADF,
99
119
  fd_read: () => WASI_BADF,
120
+ fd_readdir: () => WASI_BADF,
100
121
  fd_seek: () => WASI_BADF,
122
+ fd_sync: () => WASI_BADF,
123
+ fd_tell: () => WASI_BADF,
101
124
  fd_write: (_fd, iovsPtr, iovsLen, writtenPtr) => {
102
125
  const view = new DataView(host.bytes().buffer);
103
126
  let written = 0;
@@ -107,7 +130,39 @@ function wasiImports(host) {
107
130
  host.writeU32(writtenPtr, written);
108
131
  return 0;
109
132
  },
133
+ path_create_directory: () => WASI_BADF,
134
+ path_filestat_get: () => WASI_BADF,
135
+ path_filestat_set_times: () => WASI_BADF,
136
+ path_link: () => WASI_BADF,
110
137
  path_open: () => WASI_BADF,
138
+ path_readlink: () => WASI_BADF,
139
+ path_remove_directory: () => WASI_BADF,
140
+ path_rename: () => WASI_BADF,
141
+ path_symlink: () => WASI_BADF,
142
+ path_unlink_file: () => WASI_BADF,
143
+ // the 6.3 sdk's libraries call the clocks even when the app never reads
144
+ // the time, so instantiation fails without them. clock 0 is realtime
145
+ // and clock 1 is monotonic, the only two swift asks for here; anything
146
+ // else is a loud inval. the resolutions are wasi-libc's for those two:
147
+ // a millisecond for the wall clock and a nanosecond for the monotonic one.
148
+ clock_res_get: (clockId, resolutionPtr) => {
149
+ const resolution = clockId === 1 ? 1n : clockId === 0 ? 1000000n : null;
150
+ if (resolution === null) return WASI_INVAL;
151
+ host.writeU64(resolutionPtr, resolution);
152
+ return 0;
153
+ },
154
+ clock_time_get: (clockId, _precision, timePtr) => {
155
+ const nanos = clockId === 1 ? BigInt(Math.round(performance.now() * 1e6)) : clockId === 0 ? BigInt(Date.now()) * 1000000n : null;
156
+ if (nanos === null) return WASI_INVAL;
157
+ host.writeU64(timePtr, nanos);
158
+ return 0;
159
+ },
160
+ // a poll is the one call the tenant cannot answer: there is no descriptor
161
+ // to wait on and no clock to sleep against, so it says so instead of
162
+ // returning an event nobody subscribed to.
163
+ poll_oneoff: () => {
164
+ throw new Error("swift app polled for events, which the tenant has no source for");
165
+ },
111
166
  proc_exit: (code) => {
112
167
  throw new Error(`swift app exited with code ${code}`);
113
168
  },
@@ -150,6 +205,9 @@ function instantiate(module2) {
150
205
  writeU32(ptr, value) {
151
206
  new DataView(requireMemory().buffer).setUint32(ptr, value, true);
152
207
  },
208
+ writeU64(ptr, value) {
209
+ new DataView(requireMemory().buffer).setBigUint64(ptr, value, true);
210
+ },
153
211
  bytes: () => new Uint8Array(requireMemory().buffer)
154
212
  };
155
213
  const instance = new WebAssembly.Instance(module2, {
@@ -281,24 +339,41 @@ function resolveModifier(modifier) {
281
339
  return { ...modifier, color: resolveColor(color) };
282
340
  }
283
341
  var NODE_COMPONENTS = {
342
+ Alert: SwiftAlert,
343
+ BottomSheet: import_ui.BottomSheet,
284
344
  Button: import_ui.Button,
285
345
  Capsule: import_ui.Capsule,
286
346
  Circle: import_ui.Circle,
347
+ ConfirmationDialog: import_ui.ConfirmationDialog,
348
+ ContextMenu: import_ui.ContextMenu,
349
+ DatePicker: import_ui.DatePicker,
287
350
  Divider: import_ui.Divider,
351
+ Ellipse: import_ui.Ellipse,
288
352
  Form: import_ui.Form,
353
+ GlassEffectContainer: import_ui.GlassEffectContainer,
289
354
  Group: import_ui.Group,
290
355
  HStack: import_ui.HStack,
291
356
  Image: import_ui.Image,
357
+ Label: import_ui.Label,
358
+ LazyVStack: import_ui.LazyVStack,
292
359
  List: import_ui.List,
360
+ Menu: import_ui.Menu,
361
+ Picker: import_ui.Picker,
362
+ ProgressView: import_ui.ProgressView,
293
363
  Rectangle: import_ui.Rectangle,
294
364
  RoundedRectangle: import_ui.RoundedRectangle,
295
365
  ScrollView: import_ui.ScrollView,
296
366
  Section: import_ui.Section,
367
+ SecureField: import_ui.SecureField,
297
368
  Slider: import_ui.Slider,
298
369
  Spacer: import_ui.Spacer,
370
+ Stepper: import_ui.Stepper,
371
+ SwipeActions: import_ui.SwipeActions,
299
372
  Text: import_ui.Text,
300
373
  TextField: import_ui.TextField,
301
374
  Toggle: import_ui.Toggle,
375
+ Tab: SwiftTabScene,
376
+ TabView: SwiftTabView,
302
377
  VStack: import_ui.VStack,
303
378
  ZStack: import_ui.ZStack
304
379
  };
@@ -346,9 +421,142 @@ function fontStyle(font) {
346
421
  }
347
422
  return style;
348
423
  }
424
+ function sameValue(a, b) {
425
+ if (a === b) return true;
426
+ if (Array.isArray(a) && Array.isArray(b)) {
427
+ if (a.length !== b.length) return false;
428
+ for (let index = 0; index < a.length; index += 1) {
429
+ if (!sameValue(a[index], b[index])) return false;
430
+ }
431
+ return true;
432
+ }
433
+ if (isRecord(a) && isRecord(b)) {
434
+ const keys = Object.keys(a);
435
+ if (keys.length !== Object.keys(b).length) return false;
436
+ for (const key of keys) {
437
+ if (!sameValue(a[key], b[key])) return false;
438
+ }
439
+ return true;
440
+ }
441
+ return false;
442
+ }
443
+ function sameNode(a, b) {
444
+ if (a === b) return true;
445
+ if (a.t !== b.t || a.id !== b.id) return false;
446
+ if (!sameValue(a.p, b.p) || !sameValue(a.m, b.m) || !sameValue(a.h, b.h)) return false;
447
+ const left = a.c ?? [];
448
+ const right = b.c ?? [];
449
+ if (left.length !== right.length) return false;
450
+ for (let index = 0; index < left.length; index += 1) {
451
+ if (!sameNode(left[index], right[index])) return false;
452
+ }
453
+ return true;
454
+ }
349
455
  function handlerId(node, name) {
350
456
  return node.h?.[name];
351
457
  }
458
+ function pressProps(node, deliver) {
459
+ const presses = [];
460
+ for (const name of ["onPress", "onTap"]) {
461
+ const id = handlerId(node, name);
462
+ if (id !== void 0) presses.push(id);
463
+ }
464
+ if (presses.length === 0) return {};
465
+ return {
466
+ onPress: () => {
467
+ for (const id of presses) deliver(id, null);
468
+ }
469
+ };
470
+ }
471
+ function SwiftTabScene({ children }) {
472
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_jsx_runtime.Fragment, { children });
473
+ }
474
+ function SwiftTabView({
475
+ tabs,
476
+ selection,
477
+ onSelect,
478
+ modifiers,
479
+ children
480
+ }) {
481
+ const scenes = import_react.Children.toArray(children);
482
+ const routes = tabs.map((tab, index2) => ({
483
+ key: tab.testID,
484
+ name: tab.title ?? `tab-${index2}`,
485
+ title: tab.title,
486
+ sfSymbol: tab.systemImage,
487
+ testID: tab.testID
488
+ }));
489
+ const index = selection >= 0 && selection < routes.length ? selection : 0;
490
+ (0, import_react.useEffect)(() => {
491
+ if (selection !== index) onSelect?.(index);
492
+ }, [selection, index, onSelect]);
493
+ const bar = (0, import_react.createElement)(import_react_native_bottom_tabs.default, {
494
+ navigationState: { index, routes },
495
+ renderScene: ({ route }) => scenes[routes.findIndex((candidate) => candidate.key === route.key)],
496
+ onIndexChange: (next) => onSelect?.(next),
497
+ getLabelText: ({ route }) => route.title ?? route.key,
498
+ getIcon: ({ route }) => route.sfSymbol ? { sfSymbol: route.sfSymbol } : void 0,
499
+ getTestID: ({ route }) => route.testID
500
+ });
501
+ return (0, import_react.createElement)(
502
+ import_ui.VStack,
503
+ {
504
+ ...modifiers?.length ? { modifiers } : {},
505
+ style: {
506
+ alignSelf: "stretch",
507
+ alignItems: "stretch",
508
+ flexGrow: 1,
509
+ flexShrink: 1,
510
+ flexBasis: "auto"
511
+ }
512
+ },
513
+ bar
514
+ );
515
+ }
516
+ function SwiftAlert({
517
+ title,
518
+ message,
519
+ buttons,
520
+ onButtonPress,
521
+ onDismiss
522
+ }) {
523
+ const latest = (0, import_react.useRef)({ title, message, buttons, onButtonPress, onDismiss });
524
+ (0, import_react.useEffect)(() => {
525
+ latest.current = { title, message, buttons, onButtonPress, onDismiss };
526
+ });
527
+ (0, import_react.useEffect)(() => {
528
+ const handle = (0, import_runtime.getNativeAlerts)().show(
529
+ latest.current.title,
530
+ latest.current.message,
531
+ latest.current.buttons.map((button, index) => ({
532
+ text: button.text,
533
+ style: button.role === "destructive" ? "destructive" : button.role === "cancel" ? "cancel" : "default",
534
+ // the tapped action runs its swift closure and clears isPresented,
535
+ // which is the order the system alert uses on the way out: the action
536
+ // body is the last thing that runs with the alert still up. the
537
+ // handlers come off the ref at tap time, not from this render.
538
+ onPress: () => {
539
+ latest.current.onButtonPress?.(index);
540
+ latest.current.onDismiss?.();
541
+ }
542
+ }))
543
+ );
544
+ return () => {
545
+ (0, import_runtime.getNativeAlerts)().dismiss(handle);
546
+ };
547
+ }, []);
548
+ return null;
549
+ }
550
+ function readAlertButtons(value) {
551
+ if (!Array.isArray(value)) return [];
552
+ return value.map((entry) => {
553
+ if (!isRecord(entry)) return {};
554
+ return {
555
+ ...typeof entry.text === "string" ? { text: entry.text } : {},
556
+ ...typeof entry.role === "string" ? { role: entry.role } : {}
557
+ };
558
+ });
559
+ }
352
560
  function nodeProps(node, deliver) {
353
561
  const props = node.p ?? {};
354
562
  switch (node.t) {
@@ -356,7 +564,8 @@ function nodeProps(node, deliver) {
356
564
  const style = fontStyle(fontModifier(node.m));
357
565
  return {
358
566
  children: typeof props.children === "string" ? props.children : "",
359
- ...style ? { style } : {}
567
+ ...style ? { style } : {},
568
+ ...pressProps(node, deliver)
360
569
  };
361
570
  }
362
571
  case "Image": {
@@ -364,14 +573,18 @@ function nodeProps(node, deliver) {
364
573
  const size = typeof font?.size === "number" ? font.size : void 0;
365
574
  return {
366
575
  ...typeof props.systemName === "string" ? { systemName: props.systemName } : {},
367
- ...size === void 0 ? {} : { size }
576
+ ...size === void 0 ? {} : { size },
577
+ ...pressProps(node, deliver)
368
578
  };
369
579
  }
370
580
  case "Toggle": {
371
581
  const onValueChange = handlerId(node, "onValueChange");
372
582
  return {
373
583
  isOn: props.value === true,
374
- ...onValueChange === void 0 ? {} : { onIsOnChange: (value) => deliver(onValueChange, value) }
584
+ ...onValueChange === void 0 ? {} : { onIsOnChange: (value) => deliver(onValueChange, value) },
585
+ // the gesture replaces the row's own press (a label tap runs it and
586
+ // does not toggle); the switch keeps toggling through onValueChange.
587
+ ...pressProps(node, deliver)
375
588
  };
376
589
  }
377
590
  case "TextField": {
@@ -388,16 +601,261 @@ function nodeProps(node, deliver) {
388
601
  value: typeof props.value === "number" ? props.value : 0,
389
602
  min: typeof props.min === "number" ? props.min : 0,
390
603
  max: typeof props.max === "number" ? props.max : 1,
391
- ...onValueChange === void 0 ? {} : { onValueChange: (value) => deliver(onValueChange, value) }
604
+ ...onValueChange === void 0 ? {} : { onValueChange: (value) => deliver(onValueChange, value) },
605
+ // a tap runs the gesture and leaves the value alone: only a drag
606
+ // moves it, so the gesture rides onPress beside the drag handler.
607
+ ...pressProps(node, deliver)
608
+ };
609
+ }
610
+ case "Button":
611
+ return pressProps(node, deliver);
612
+ case "TabView": {
613
+ const onSelect = handlerId(node, "onSelect");
614
+ const tabs = (node.c ?? []).map((tab, index) => {
615
+ const tabProps = tab.p ?? {};
616
+ return {
617
+ ...typeof tabProps.title === "string" ? { title: tabProps.title } : {},
618
+ ...typeof tabProps.systemImage === "string" ? { systemImage: tabProps.systemImage } : {},
619
+ ...typeof tabProps.role === "string" ? { role: tabProps.role } : {},
620
+ testID: `swift-tab-${index}`
621
+ };
622
+ });
623
+ return {
624
+ tabs,
625
+ selection: typeof props.selection === "number" ? props.selection : 0,
626
+ ...onSelect === void 0 ? {} : { onSelect: (index) => deliver(onSelect, index) }
627
+ };
628
+ }
629
+ case "BottomSheet": {
630
+ const onDismiss = handlerId(node, "onDismiss");
631
+ return {
632
+ isOpened: props.isOpened === true,
633
+ ...onDismiss === void 0 ? {} : { onDismiss: () => deliver(onDismiss, null) }
634
+ };
635
+ }
636
+ case "ConfirmationDialog": {
637
+ return { isPresented: props.isPresented === true };
638
+ }
639
+ case "Alert": {
640
+ const onButtonPress = handlerId(node, "onButtonPress");
641
+ const onDismiss = handlerId(node, "onDismiss");
642
+ return {
643
+ title: typeof props.title === "string" ? props.title : "",
644
+ ...typeof props.message === "string" ? { message: props.message } : {},
645
+ buttons: readAlertButtons(props.buttons),
646
+ ...onButtonPress === void 0 ? {} : { onButtonPress: (index) => deliver(onButtonPress, index) },
647
+ ...onDismiss === void 0 ? {} : { onDismiss: () => deliver(onDismiss, null) }
648
+ };
649
+ }
650
+ case "SecureField": {
651
+ const onChangeText = handlerId(node, "onChangeText");
652
+ return {
653
+ ...typeof props.placeholder === "string" ? { placeholder: props.placeholder } : {},
654
+ ...typeof props.prompt === "string" ? { prompt: props.prompt } : {},
655
+ ...typeof props.defaultValue === "string" ? { defaultValue: props.defaultValue } : {},
656
+ ...onChangeText === void 0 ? {} : { onChangeText: (value) => deliver(onChangeText, value) }
657
+ };
658
+ }
659
+ case "Picker": {
660
+ const onSelectionChange = handlerId(node, "onSelectionChange");
661
+ return {
662
+ ...typeof props.title === "string" ? { label: props.title } : {},
663
+ ...props.selection === void 0 ? {} : { selection: props.selection },
664
+ ...onSelectionChange === void 0 ? {} : {
665
+ onSelectionChange: (value) => deliver(onSelectionChange, value)
666
+ }
392
667
  };
393
668
  }
394
- case "Button": {
395
- const onPress = handlerId(node, "onPress");
396
- return onPress === void 0 ? {} : { onPress: () => deliver(onPress, null) };
669
+ case "DatePicker": {
670
+ return {
671
+ ...typeof props.title === "string" ? { label: props.title } : {},
672
+ // the wire carries epoch seconds; the kit takes a Date and formats it
673
+ ...typeof props.value === "number" ? { value: new Date(props.value * 1e3) } : {},
674
+ ...Array.isArray(props.components) ? { components: props.components } : {}
675
+ };
397
676
  }
677
+ case "GlassEffectContainer":
678
+ return {};
679
+ case "ProgressView":
680
+ return {
681
+ ...typeof props.title === "string" ? { label: props.title } : {},
682
+ ...typeof props.value === "number" || props.value === null ? { value: props.value } : {},
683
+ ...typeof props.total === "number" ? { total: props.total } : {}
684
+ };
398
685
  default:
399
- return props;
686
+ return { ...props, ...pressProps(node, deliver) };
687
+ }
688
+ }
689
+ function numberProp(node, name) {
690
+ const value = node.p?.[name];
691
+ return typeof value === "number" ? value : 0;
692
+ }
693
+ function menuItemLabel(node) {
694
+ const [only] = node.c ?? [];
695
+ if (node.t !== "Button" || node.c?.length !== 1 || !only) return void 0;
696
+ if (only.t === "Text" && typeof only.p?.children === "string") {
697
+ return { label: only.p.children };
698
+ }
699
+ if (only.t === "Label" && typeof only.p?.title === "string" && (only.p?.systemImage === void 0 || typeof only.p?.systemImage === "string")) {
700
+ return {
701
+ label: only.p.title,
702
+ ...typeof only.p?.systemImage === "string" ? { systemImage: only.p.systemImage } : {}
703
+ };
400
704
  }
705
+ return void 0;
706
+ }
707
+ function renderMenuItem(node, deliver, cache) {
708
+ const item = menuItemLabel(node);
709
+ if (item !== void 0) {
710
+ const modifiers = nodeModifiers(node);
711
+ return (0, import_react.createElement)(import_ui.Button, {
712
+ key: node.id,
713
+ ...item,
714
+ ...modifiers === void 0 ? {} : { modifiers },
715
+ ...pressProps(node, deliver)
716
+ });
717
+ }
718
+ return renderNode(node, deliver, cache);
719
+ }
720
+ function renderMenu(node, deliver, cache) {
721
+ const labelCount = numberProp(node, "labelCount");
722
+ const kids = node.c ?? [];
723
+ const labels = kids.slice(0, labelCount);
724
+ const items = kids.slice(labelCount);
725
+ const [only] = labels;
726
+ const onlyText = labels.length === 1 && only.t === "Text" && typeof only.p?.children === "string" ? only.p.children : void 0;
727
+ const onlyLabel = labels.length === 1 && only.t === "Label" && typeof only.p?.title === "string" && (only.p?.systemImage === void 0 || typeof only.p?.systemImage === "string") ? only : void 0;
728
+ const label = onlyText !== void 0 || onlyLabel !== void 0 ? void 0 : (0, import_react.createElement)(
729
+ import_react.Fragment,
730
+ null,
731
+ ...labels.map((child) => renderNode(child, deliver, cache))
732
+ );
733
+ const labelModifiers = labels.length === 1 ? nodeModifiers(only) : void 0;
734
+ const modifiers = nodeModifiers(node);
735
+ const combined = [...labelModifiers ?? [], ...modifiers ?? []];
736
+ return (0, import_react.createElement)(
737
+ import_ui.Menu,
738
+ {
739
+ key: node.id,
740
+ ...combined.length > 0 ? { modifiers: combined } : {},
741
+ ...onlyText !== void 0 ? { label: onlyText } : {},
742
+ ...onlyLabel !== void 0 ? {
743
+ label: onlyLabel.p?.title,
744
+ ...typeof onlyLabel.p?.systemImage === "string" ? { systemImage: onlyLabel.p.systemImage } : {}
745
+ } : {},
746
+ ...label === void 0 ? {} : { label }
747
+ },
748
+ ...items.map((child) => renderMenuItem(child, deliver))
749
+ );
750
+ }
751
+ function renderContextMenu(node, deliver, cache) {
752
+ const kids = node.c ?? [];
753
+ const contentCount = numberProp(node, "contentCount");
754
+ const itemCount = numberProp(node, "itemCount");
755
+ const content = kids.slice(0, contentCount);
756
+ const items = kids.slice(contentCount, contentCount + itemCount);
757
+ const preview = kids.slice(contentCount + itemCount);
758
+ const modifiers = nodeModifiers(node);
759
+ return (0, import_react.createElement)(
760
+ import_ui.ContextMenu,
761
+ {
762
+ key: node.id,
763
+ ...modifiers === void 0 ? {} : { modifiers }
764
+ },
765
+ (0, import_react.createElement)(
766
+ import_ui.Trigger,
767
+ { key: `${node.id}/trigger` },
768
+ ...content.map((child) => renderNode(child, deliver, cache))
769
+ ),
770
+ (0, import_react.createElement)(
771
+ import_ui.Items,
772
+ { key: `${node.id}/items` },
773
+ ...items.map((child) => renderMenuItem(child, deliver))
774
+ ),
775
+ ...preview.length > 0 ? [
776
+ (0, import_react.createElement)(
777
+ import_ui.Preview,
778
+ { key: `${node.id}/preview` },
779
+ ...preview.map((child) => renderNode(child, deliver, cache))
780
+ )
781
+ ] : []
782
+ );
783
+ }
784
+ function renderSwipeActions(node, deliver, cache) {
785
+ const kids = node.c ?? [];
786
+ const content = kids.slice(0, numberProp(node, "contentCount"));
787
+ const actions = kids.slice(numberProp(node, "contentCount"));
788
+ const modifiers = nodeModifiers(node);
789
+ const { Actions } = import_ui.SwipeActions;
790
+ return (0, import_react.createElement)(
791
+ import_ui.SwipeActions,
792
+ {
793
+ key: node.id,
794
+ ...modifiers === void 0 ? {} : { modifiers }
795
+ },
796
+ ...content.map((child) => renderNode(child, deliver, cache)),
797
+ ...actions.length > 0 ? [
798
+ (0, import_react.createElement)(
799
+ Actions,
800
+ {
801
+ key: `${node.id}/actions`,
802
+ edge: node.p?.edge === "leading" ? "leading" : "trailing",
803
+ ...typeof node.p?.allowsFullSwipe === "boolean" ? { allowsFullSwipe: node.p.allowsFullSwipe } : {}
804
+ },
805
+ ...actions.map((child) => renderNode(child, deliver, cache))
806
+ )
807
+ ] : []
808
+ );
809
+ }
810
+ function ClosureStepper({
811
+ deliver,
812
+ onIncrement,
813
+ onDecrement,
814
+ ...rest
815
+ }) {
816
+ const last = (0, import_react.useRef)(0);
817
+ return (0, import_react.createElement)(import_ui.Stepper, {
818
+ ...rest,
819
+ onValueChange: (next) => {
820
+ if (next > last.current && onIncrement !== void 0) {
821
+ deliver(onIncrement, null);
822
+ } else if (next < last.current && onDecrement !== void 0) {
823
+ deliver(onDecrement, null);
824
+ }
825
+ last.current = next;
826
+ }
827
+ });
828
+ }
829
+ function renderStepper(node, deliver, cache) {
830
+ const kids = node.c ?? [];
831
+ const label = kids.length === 0 ? void 0 : (0, import_react.createElement)(
832
+ import_react.Fragment,
833
+ null,
834
+ ...kids.map((child) => renderNode(child, deliver, cache))
835
+ );
836
+ const modifiers = nodeModifiers(node);
837
+ const shared = {
838
+ key: node.id,
839
+ ...modifiers === void 0 ? {} : { modifiers },
840
+ ...label === void 0 ? {} : { label },
841
+ ...typeof node.p?.min === "number" ? { min: node.p.min } : {},
842
+ ...typeof node.p?.max === "number" ? { max: node.p.max } : {},
843
+ ...typeof node.p?.step === "number" ? { step: node.p.step } : {}
844
+ };
845
+ const onValueChange = handlerId(node, "onValueChange");
846
+ if (onValueChange !== void 0) {
847
+ return (0, import_react.createElement)(import_ui.Stepper, {
848
+ ...shared,
849
+ ...typeof node.p?.value === "number" ? { value: node.p.value } : {},
850
+ onValueChange: (value) => deliver(onValueChange, value)
851
+ });
852
+ }
853
+ return (0, import_react.createElement)(ClosureStepper, {
854
+ ...shared,
855
+ deliver,
856
+ ...handlerId(node, "onIncrement") === void 0 ? {} : { onIncrement: handlerId(node, "onIncrement") },
857
+ ...handlerId(node, "onDecrement") === void 0 ? {} : { onDecrement: handlerId(node, "onDecrement") }
858
+ });
401
859
  }
402
860
  function buttonModifier(modifier) {
403
861
  if (modifier.$type === "glassEffect") {
@@ -415,11 +873,195 @@ function nodeModifiers(node) {
415
873
  if (node.t !== "Button") return resolved;
416
874
  return resolved?.map(buttonModifier);
417
875
  }
418
- function renderNode(node, deliver) {
876
+ function dialogChildren(node, deliver) {
877
+ const children = node.c?.map((child) => renderNode(child, deliver));
878
+ const props = node.p ?? {};
879
+ if (props.showsTitle !== true || typeof props.title !== "string") return children;
880
+ const title = (0, import_react.createElement)(import_ui.Text, { key: `${node.id}/title`, children: props.title });
881
+ return [title, ...children ?? []];
882
+ }
883
+ var Stack = (0, import_native_stack.createNativeStackNavigator)();
884
+ var NAVIGATOR_FILL = { flex: 1, alignSelf: "stretch" };
885
+ function readNodes(value) {
886
+ if (!Array.isArray(value)) return [];
887
+ return value.map(readNode);
888
+ }
889
+ function hasPrefix(names, of) {
890
+ return names.every((name, index) => of[index] === name);
891
+ }
892
+ function sameNames(a, b) {
893
+ return a.length === b.length && a.every((name, index) => b[index] === name);
894
+ }
895
+ function commonPrefixLength(a, b) {
896
+ let length = 0;
897
+ while (length < a.length && length < b.length && a[length] === b[length]) length++;
898
+ return length;
899
+ }
900
+ function reconcilePlan(names, desired) {
901
+ const common = commonPrefixLength(names, desired);
902
+ if (common === names.length && common === desired.length) return { kind: "in-step" };
903
+ if (common === names.length) return { kind: "push" };
904
+ if (common === desired.length) return { kind: "pop" };
905
+ const top = desired[desired.length - 1];
906
+ return { kind: "replace", replaceAnimation: names.includes(top) ? "pop" : "push" };
907
+ }
908
+ function resolveLargeTitles(modes) {
909
+ let previous = true;
910
+ return modes.map((mode) => {
911
+ const large = mode === "inline" ? false : mode === "large" ? true : previous;
912
+ previous = large;
913
+ return large;
914
+ });
915
+ }
916
+ function screenOptions(node, deliver, largeTitle, replaceAnimation) {
917
+ const props = node.p ?? {};
918
+ const options = {
919
+ title: typeof props.title === "string" ? props.title : "",
920
+ headerLargeTitleEnabled: largeTitle
921
+ };
922
+ if (replaceAnimation !== void 0) options.animationTypeForReplace = replaceAnimation;
923
+ const leading = readNodes(props.leading);
924
+ const trailing = readNodes(props.trailing);
925
+ const principal = readNodes(props.principal);
926
+ const render = (nodes) => () => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_jsx_runtime.Fragment, { children: nodes.map((child) => renderNode(child, deliver)) });
927
+ if (leading.length > 0) {
928
+ options.headerLeft = render(leading);
929
+ options.headerBackVisible = true;
930
+ }
931
+ if (trailing.length > 0) options.headerRight = render(trailing);
932
+ if (principal.length > 0) options.headerTitle = render(principal);
933
+ return options;
934
+ }
935
+ function SwiftScreen({
936
+ node,
937
+ navigation,
938
+ deliver,
939
+ report
940
+ }) {
941
+ const routes = (0, import_native.useNavigationState)(
942
+ (state) => state.routes.map((route) => route.name).join("\0")
943
+ );
944
+ (0, import_react.useEffect)(() => {
945
+ report(navigation, routes === "" ? [] : routes.split("\0"));
946
+ });
947
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_jsx_runtime.Fragment, { children: node.c?.map((child) => renderNode(child, deliver)) });
948
+ }
949
+ function NavigationStackHost({ node, deliver }) {
950
+ const screens = (node.c ?? []).filter((child) => child.t === "NavigationScreen");
951
+ const desired = screens.map((screen) => screen.id);
952
+ const largeTitles = resolveLargeTitles(
953
+ screens.map((screen) => {
954
+ const mode = screen.p?.titleDisplayMode;
955
+ return typeof mode === "string" ? mode : void 0;
956
+ })
957
+ );
958
+ const onPop = node.h?.onPop;
959
+ const reportRef = (0, import_react.useRef)(null);
960
+ const reportedRef = (0, import_react.useRef)([]);
961
+ const expectedRef = (0, import_react.useRef)(null);
962
+ const syncedRef = (0, import_react.useRef)(null);
963
+ const [, rerender] = (0, import_react.useReducer)((count) => count + 1, 0);
964
+ const report = (0, import_react.useCallback)((navigation, names) => {
965
+ const previous = reportRef.current;
966
+ reportRef.current = { navigation, names };
967
+ if (previous && sameNames(previous.names, names)) return;
968
+ rerender();
969
+ }, []);
970
+ const reported = reportRef.current?.names ?? [];
971
+ const plan = reportRef.current ? reconcilePlan(reported, desired) : null;
972
+ const replaceTop = plan?.kind === "replace" ? desired[desired.length - 1] : void 0;
973
+ (0, import_react.useEffect)(() => {
974
+ const current = reportRef.current;
975
+ if (!current) return;
976
+ const names = current.names;
977
+ const previous = reportedRef.current;
978
+ const isReport = !sameNames(previous, names);
979
+ if (isReport) reportedRef.current = names;
980
+ const expected = expectedRef.current;
981
+ if (expected) {
982
+ if (sameNames(expected, names)) expectedRef.current = null;
983
+ else if (!isReport) return;
984
+ else expectedRef.current = null;
985
+ }
986
+ const dropped = previous.slice(names.length);
987
+ const settled = syncedRef.current;
988
+ const navigatorPopped = isReport && dropped.length > 0 && settled !== null && sameNames(settled, desired) && hasPrefix(names, desired) && hasPrefix(desired.slice(names.length), dropped);
989
+ if (navigatorPopped && onPop !== void 0) {
990
+ deliver(onPop, { count: dropped.length });
991
+ return;
992
+ }
993
+ const next = reconcilePlan(names, desired);
994
+ if (next.kind === "in-step") {
995
+ expectedRef.current = null;
996
+ syncedRef.current = desired;
997
+ return;
998
+ }
999
+ expectedRef.current = desired;
1000
+ if (next.kind === "push") {
1001
+ for (const name of desired.slice(names.length)) current.navigation.push(name);
1002
+ return;
1003
+ }
1004
+ if (next.kind === "pop") {
1005
+ current.navigation.pop(names.length - desired.length);
1006
+ return;
1007
+ }
1008
+ current.navigation.reset({
1009
+ index: Math.max(0, desired.length - 1),
1010
+ routes: desired.map((name) => ({ name }))
1011
+ });
1012
+ });
1013
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Stack.Navigator, { initialRouteName: desired[0], children: screens.map((screen, index) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1014
+ Stack.Screen,
1015
+ {
1016
+ name: screen.id,
1017
+ options: screenOptions(
1018
+ screen,
1019
+ deliver,
1020
+ largeTitles[index],
1021
+ screen.id === replaceTop ? plan?.kind === "replace" ? plan.replaceAnimation : void 0 : void 0
1022
+ ),
1023
+ children: (props) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1024
+ SwiftScreen,
1025
+ {
1026
+ node: screen,
1027
+ navigation: props.navigation,
1028
+ deliver,
1029
+ report
1030
+ }
1031
+ )
1032
+ },
1033
+ screen.id
1034
+ )) });
1035
+ }
1036
+ function renderNode(node, deliver, cache) {
1037
+ const cached = cache?.entries.get(node.id);
1038
+ if (cached && sameNode(cached.node, node)) return cached.element;
1039
+ const element = buildNode(node, deliver, cache);
1040
+ cache?.entries.set(node.id, { node, element });
1041
+ return element;
1042
+ }
1043
+ function buildNode(node, deliver, cache) {
1044
+ if (node.t === "Menu") return renderMenu(node, deliver, cache);
1045
+ if (node.t === "ContextMenu") return renderContextMenu(node, deliver, cache);
1046
+ if (node.t === "SwipeActions") return renderSwipeActions(node, deliver, cache);
1047
+ if (node.t === "Stepper") return renderStepper(node, deliver, cache);
1048
+ if (node.t === "NavigationStack") {
1049
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react_native.View, { style: NAVIGATOR_FILL, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(NavigationStackHost, { node, deliver }) }, node.id);
1050
+ }
1051
+ if (node.t === "NavigationLink") {
1052
+ return (0, import_react.createElement)(
1053
+ import_ui.Button,
1054
+ {
1055
+ key: node.id,
1056
+ ...pressProps(node, deliver)
1057
+ },
1058
+ ...(node.c ?? []).map((child) => renderNode(child, deliver, cache))
1059
+ );
1060
+ }
419
1061
  const component = NODE_COMPONENTS[node.t];
420
1062
  if (!component) throw new Error(`swift view ${node.t} has no rnx kit component`);
421
1063
  const modifiers = nodeModifiers(node);
422
- const children = node.c?.map((child) => renderNode(child, deliver));
1064
+ const children = node.t === "ConfirmationDialog" ? dialogChildren(node, deliver) : node.c?.map((child) => renderNode(child, deliver));
423
1065
  return (0, import_react.createElement)(
424
1066
  component,
425
1067
  {
@@ -430,6 +1072,25 @@ function renderNode(node, deliver) {
430
1072
  ...children ?? []
431
1073
  );
432
1074
  }
1075
+ function SwiftTree({
1076
+ tree,
1077
+ deliver,
1078
+ cache
1079
+ }) {
1080
+ return (
1081
+ // a navigation stack at the root lays its own bar out under the status
1082
+ // bar and insets each screen itself, so the host must not also pad it
1083
+ // down: that pad would move the whole stack a status bar lower
1084
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1085
+ import_ui.Host,
1086
+ {
1087
+ style: { flex: 1 },
1088
+ ignoreSafeArea: tree.some((node) => node.t === "NavigationStack"),
1089
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_native.NavigationIndependentTree, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_native.NavigationContainer, { children: tree.map((node) => renderNode(node, deliver, cache)) }) })
1090
+ }
1091
+ )
1092
+ );
1093
+ }
433
1094
  var ROOT_KEY = "__rnx_swift_roots__";
434
1095
  function rootRegistry() {
435
1096
  const existing = Reflect.get(globalThis, ROOT_KEY);
@@ -444,6 +1105,7 @@ function createRoot() {
444
1105
  let state = { tree: null, error: null };
445
1106
  let swap = Promise.resolve();
446
1107
  const listeners = /* @__PURE__ */ new Set();
1108
+ const cache = { entries: /* @__PURE__ */ new Map() };
447
1109
  const publish = (next) => {
448
1110
  state = next;
449
1111
  for (const listener of listeners) listener();
@@ -470,7 +1132,7 @@ function createRoot() {
470
1132
  if (!instance) throw new Error("swift app received an event before it mounted");
471
1133
  publish({ tree: instance.deliver(handlerId2, payload), error: null });
472
1134
  };
473
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_ui.Host, { style: { flex: 1 }, children: rendered.tree.map((node) => renderNode(node, deliver)) });
1135
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SwiftTree, { tree: rendered.tree, deliver, cache });
474
1136
  };
475
1137
  return {
476
1138
  component,