rnxsim 0.1.519 → 0.1.521

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 (71) 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 +7 -2
  20. package/dist-lib/bridge-contract-input.mjs +7 -2
  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 +18 -2
  29. package/dist-lib/cloud.mjs +18 -2
  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 +44 -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 +766 -24
  60. package/dist-lib/vite.cjs +103 -30
  61. package/package.json +1 -1
  62. package/scripts/dev-server-scanner.ts +72 -5
  63. package/src/bridge-contract-input.ts +10 -0
  64. package/src/bridge-contract.ts +5 -0
  65. package/src/cloud-contract.ts +7 -1
  66. package/src/cloud.ts +45 -6
  67. package/src/connect.ts +1 -1
  68. package/src/menu.ts +44 -0
  69. package/src/swift.ts +2 -0
  70. package/src/vite-plugin-one.ts +12 -0
  71. package/src/vite-plugin-swift.ts +141 -39
@@ -1,9 +1,11 @@
1
- /*! rnx v0.1.519 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.521 | (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,19 +19,32 @@ 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
23
33
  var swift_exports = {};
24
34
  __export(swift_exports, {
25
- createSwiftModule: () => createSwiftModule
35
+ createSwiftModule: () => createSwiftModule,
36
+ createSwiftView: () => createSwiftView
26
37
  });
27
38
  module.exports = __toCommonJS(swift_exports);
28
39
 
29
40
  // ../sootsim-swift/src/mount.tsx
41
+ var import_native = require("@react-navigation/native");
42
+ var import_native_stack = require("@react-navigation/native-stack");
43
+ var import_runtime = require("contrast-kit/runtime");
30
44
  var import_ui = require("contrast-kit/ui");
31
45
  var import_react = require("react");
32
46
  var import_react_native = require("react-native");
47
+ var import_react_native_bottom_tabs = __toESM(require("react-native-bottom-tabs"), 1);
33
48
  var import_jsx_runtime = require("react/jsx-runtime");
34
49
  function isRecord(value) {
35
50
  return typeof value === "object" && value !== null;
@@ -78,6 +93,7 @@ function readTree(text) {
78
93
  return value.map(readNode);
79
94
  }
80
95
  var WASI_BADF = 8;
96
+ var WASI_INVAL = 28;
81
97
  function wasiImports(host) {
82
98
  return {
83
99
  args_get: () => 0,
@@ -94,10 +110,18 @@ function wasiImports(host) {
94
110
  },
95
111
  fd_close: () => WASI_BADF,
96
112
  fd_fdstat_get: () => WASI_BADF,
113
+ fd_fdstat_set_flags: () => WASI_BADF,
114
+ fd_filestat_get: () => WASI_BADF,
115
+ fd_filestat_set_size: () => WASI_BADF,
116
+ fd_filestat_set_times: () => WASI_BADF,
117
+ fd_pread: () => WASI_BADF,
97
118
  fd_prestat_get: () => WASI_BADF,
98
119
  fd_prestat_dir_name: () => WASI_BADF,
99
120
  fd_read: () => WASI_BADF,
121
+ fd_readdir: () => WASI_BADF,
100
122
  fd_seek: () => WASI_BADF,
123
+ fd_sync: () => WASI_BADF,
124
+ fd_tell: () => WASI_BADF,
101
125
  fd_write: (_fd, iovsPtr, iovsLen, writtenPtr) => {
102
126
  const view = new DataView(host.bytes().buffer);
103
127
  let written = 0;
@@ -107,7 +131,39 @@ function wasiImports(host) {
107
131
  host.writeU32(writtenPtr, written);
108
132
  return 0;
109
133
  },
134
+ path_create_directory: () => WASI_BADF,
135
+ path_filestat_get: () => WASI_BADF,
136
+ path_filestat_set_times: () => WASI_BADF,
137
+ path_link: () => WASI_BADF,
110
138
  path_open: () => WASI_BADF,
139
+ path_readlink: () => WASI_BADF,
140
+ path_remove_directory: () => WASI_BADF,
141
+ path_rename: () => WASI_BADF,
142
+ path_symlink: () => WASI_BADF,
143
+ path_unlink_file: () => WASI_BADF,
144
+ // the 6.3 sdk's libraries call the clocks even when the app never reads
145
+ // the time, so instantiation fails without them. clock 0 is realtime
146
+ // and clock 1 is monotonic, the only two swift asks for here; anything
147
+ // else is a loud inval. the resolutions are wasi-libc's for those two:
148
+ // a millisecond for the wall clock and a nanosecond for the monotonic one.
149
+ clock_res_get: (clockId, resolutionPtr) => {
150
+ const resolution = clockId === 1 ? 1n : clockId === 0 ? 1000000n : null;
151
+ if (resolution === null) return WASI_INVAL;
152
+ host.writeU64(resolutionPtr, resolution);
153
+ return 0;
154
+ },
155
+ clock_time_get: (clockId, _precision, timePtr) => {
156
+ const nanos = clockId === 1 ? BigInt(Math.round(performance.now() * 1e6)) : clockId === 0 ? BigInt(Date.now()) * 1000000n : null;
157
+ if (nanos === null) return WASI_INVAL;
158
+ host.writeU64(timePtr, nanos);
159
+ return 0;
160
+ },
161
+ // a poll is the one call the tenant cannot answer: there is no descriptor
162
+ // to wait on and no clock to sleep against, so it says so instead of
163
+ // returning an event nobody subscribed to.
164
+ poll_oneoff: () => {
165
+ throw new Error("swift app polled for events, which the tenant has no source for");
166
+ },
111
167
  proc_exit: (code) => {
112
168
  throw new Error(`swift app exited with code ${code}`);
113
169
  },
@@ -134,6 +190,20 @@ function pointerExport(exports2, name) {
134
190
  return result;
135
191
  };
136
192
  }
193
+ function maybePointerExport(exports2, name) {
194
+ const value = exports2[name];
195
+ if (value === void 0) return void 0;
196
+ if (typeof value !== "function") {
197
+ throw new Error(`swift export ${name} is not a function`);
198
+ }
199
+ return (...args) => {
200
+ const result = Reflect.apply(value, void 0, args);
201
+ if (typeof result !== "number") {
202
+ throw new Error(`swift export ${name} returned ${typeof result}`);
203
+ }
204
+ return result;
205
+ };
206
+ }
137
207
  function voidExport(exports2, name) {
138
208
  const fn = rawExport(exports2, name);
139
209
  return (...args) => {
@@ -150,6 +220,9 @@ function instantiate(module2) {
150
220
  writeU32(ptr, value) {
151
221
  new DataView(requireMemory().buffer).setUint32(ptr, value, true);
152
222
  },
223
+ writeU64(ptr, value) {
224
+ new DataView(requireMemory().buffer).setBigUint64(ptr, value, true);
225
+ },
153
226
  bytes: () => new Uint8Array(requireMemory().buffer)
154
227
  };
155
228
  const instance = new WebAssembly.Instance(module2, {
@@ -163,6 +236,8 @@ function instantiate(module2) {
163
236
  const initialize = voidExport(instance.exports, "_initialize");
164
237
  const mainArgv = voidExport(instance.exports, "__main_argc_argv");
165
238
  const mount = pointerExport(instance.exports, "rnx_mount");
239
+ const mountViewExport = maybePointerExport(instance.exports, "rnx_mount_view");
240
+ const hasViewExport = maybePointerExport(instance.exports, "rnx_has_view");
166
241
  const event = pointerExport(instance.exports, "rnx_event");
167
242
  const stateExport = pointerExport(instance.exports, "rnx_state_export");
168
243
  const stateImport = voidExport(instance.exports, "rnx_state_import");
@@ -186,7 +261,30 @@ function instantiate(module2) {
186
261
  initialize();
187
262
  mainArgv(0, 0);
188
263
  return {
189
- mount: () => readTree(readBuffer(mount())),
264
+ mount: () => {
265
+ if ((hasViewExport?.() ?? 0) === 1) {
266
+ throw new Error(
267
+ "swift package has an RNXPackage entry, not an App entry; mount it as a view or make its @main type conform to App"
268
+ );
269
+ }
270
+ return readTree(readBuffer(mount()));
271
+ },
272
+ mountView: (props) => {
273
+ if (!mountViewExport) {
274
+ throw new Error(
275
+ "swift artifact has no rnx_mount_view export; rebuild the swift package"
276
+ );
277
+ }
278
+ if ((hasViewExport?.() ?? 0) !== 1) {
279
+ throw new Error(
280
+ "swift package has no RNXPackage entry; its @main type must conform to RNXPackage to mount as a view"
281
+ );
282
+ }
283
+ return readTree(
284
+ readBuffer(withPayload(props, (ptr, length) => mountViewExport(ptr, length)))
285
+ );
286
+ },
287
+ hasView: () => (hasViewExport?.() ?? 0) === 1,
190
288
  deliver: (handlerId2, payload) => readTree(
191
289
  readBuffer(
192
290
  withPayload(
@@ -281,24 +379,41 @@ function resolveModifier(modifier) {
281
379
  return { ...modifier, color: resolveColor(color) };
282
380
  }
283
381
  var NODE_COMPONENTS = {
382
+ Alert: SwiftAlert,
383
+ BottomSheet: import_ui.BottomSheet,
284
384
  Button: import_ui.Button,
285
385
  Capsule: import_ui.Capsule,
286
386
  Circle: import_ui.Circle,
387
+ ConfirmationDialog: import_ui.ConfirmationDialog,
388
+ ContextMenu: import_ui.ContextMenu,
389
+ DatePicker: import_ui.DatePicker,
287
390
  Divider: import_ui.Divider,
391
+ Ellipse: import_ui.Ellipse,
288
392
  Form: import_ui.Form,
393
+ GlassEffectContainer: import_ui.GlassEffectContainer,
289
394
  Group: import_ui.Group,
290
395
  HStack: import_ui.HStack,
291
396
  Image: import_ui.Image,
397
+ Label: import_ui.Label,
398
+ LazyVStack: import_ui.LazyVStack,
292
399
  List: import_ui.List,
400
+ Menu: import_ui.Menu,
401
+ Picker: import_ui.Picker,
402
+ ProgressView: import_ui.ProgressView,
293
403
  Rectangle: import_ui.Rectangle,
294
404
  RoundedRectangle: import_ui.RoundedRectangle,
295
405
  ScrollView: import_ui.ScrollView,
296
406
  Section: import_ui.Section,
407
+ SecureField: import_ui.SecureField,
297
408
  Slider: import_ui.Slider,
298
409
  Spacer: import_ui.Spacer,
410
+ Stepper: import_ui.Stepper,
411
+ SwipeActions: import_ui.SwipeActions,
299
412
  Text: import_ui.Text,
300
413
  TextField: import_ui.TextField,
301
414
  Toggle: import_ui.Toggle,
415
+ Tab: SwiftTabScene,
416
+ TabView: SwiftTabView,
302
417
  VStack: import_ui.VStack,
303
418
  ZStack: import_ui.ZStack
304
419
  };
@@ -346,9 +461,142 @@ function fontStyle(font) {
346
461
  }
347
462
  return style;
348
463
  }
464
+ function sameValue(a, b) {
465
+ if (a === b) return true;
466
+ if (Array.isArray(a) && Array.isArray(b)) {
467
+ if (a.length !== b.length) return false;
468
+ for (let index = 0; index < a.length; index += 1) {
469
+ if (!sameValue(a[index], b[index])) return false;
470
+ }
471
+ return true;
472
+ }
473
+ if (isRecord(a) && isRecord(b)) {
474
+ const keys = Object.keys(a);
475
+ if (keys.length !== Object.keys(b).length) return false;
476
+ for (const key of keys) {
477
+ if (!sameValue(a[key], b[key])) return false;
478
+ }
479
+ return true;
480
+ }
481
+ return false;
482
+ }
483
+ function sameNode(a, b) {
484
+ if (a === b) return true;
485
+ if (a.t !== b.t || a.id !== b.id) return false;
486
+ if (!sameValue(a.p, b.p) || !sameValue(a.m, b.m) || !sameValue(a.h, b.h)) return false;
487
+ const left = a.c ?? [];
488
+ const right = b.c ?? [];
489
+ if (left.length !== right.length) return false;
490
+ for (let index = 0; index < left.length; index += 1) {
491
+ if (!sameNode(left[index], right[index])) return false;
492
+ }
493
+ return true;
494
+ }
349
495
  function handlerId(node, name) {
350
496
  return node.h?.[name];
351
497
  }
498
+ function pressProps(node, deliver) {
499
+ const presses = [];
500
+ for (const name of ["onPress", "onTap"]) {
501
+ const id = handlerId(node, name);
502
+ if (id !== void 0) presses.push(id);
503
+ }
504
+ if (presses.length === 0) return {};
505
+ return {
506
+ onPress: () => {
507
+ for (const id of presses) deliver(id, null);
508
+ }
509
+ };
510
+ }
511
+ function SwiftTabScene({ children }) {
512
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_jsx_runtime.Fragment, { children });
513
+ }
514
+ function SwiftTabView({
515
+ tabs,
516
+ selection,
517
+ onSelect,
518
+ modifiers,
519
+ children
520
+ }) {
521
+ const scenes = import_react.Children.toArray(children);
522
+ const routes = tabs.map((tab, index2) => ({
523
+ key: tab.testID,
524
+ name: tab.title ?? `tab-${index2}`,
525
+ title: tab.title,
526
+ sfSymbol: tab.systemImage,
527
+ testID: tab.testID
528
+ }));
529
+ const index = selection >= 0 && selection < routes.length ? selection : 0;
530
+ (0, import_react.useEffect)(() => {
531
+ if (selection !== index) onSelect?.(index);
532
+ }, [selection, index, onSelect]);
533
+ const bar = (0, import_react.createElement)(import_react_native_bottom_tabs.default, {
534
+ navigationState: { index, routes },
535
+ renderScene: ({ route }) => scenes[routes.findIndex((candidate) => candidate.key === route.key)],
536
+ onIndexChange: (next) => onSelect?.(next),
537
+ getLabelText: ({ route }) => route.title ?? route.key,
538
+ getIcon: ({ route }) => route.sfSymbol ? { sfSymbol: route.sfSymbol } : void 0,
539
+ getTestID: ({ route }) => route.testID
540
+ });
541
+ return (0, import_react.createElement)(
542
+ import_ui.VStack,
543
+ {
544
+ ...modifiers?.length ? { modifiers } : {},
545
+ style: {
546
+ alignSelf: "stretch",
547
+ alignItems: "stretch",
548
+ flexGrow: 1,
549
+ flexShrink: 1,
550
+ flexBasis: "auto"
551
+ }
552
+ },
553
+ bar
554
+ );
555
+ }
556
+ function SwiftAlert({
557
+ title,
558
+ message,
559
+ buttons,
560
+ onButtonPress,
561
+ onDismiss
562
+ }) {
563
+ const latest = (0, import_react.useRef)({ title, message, buttons, onButtonPress, onDismiss });
564
+ (0, import_react.useEffect)(() => {
565
+ latest.current = { title, message, buttons, onButtonPress, onDismiss };
566
+ });
567
+ (0, import_react.useEffect)(() => {
568
+ const handle = (0, import_runtime.getNativeAlerts)().show(
569
+ latest.current.title,
570
+ latest.current.message,
571
+ latest.current.buttons.map((button, index) => ({
572
+ text: button.text,
573
+ style: button.role === "destructive" ? "destructive" : button.role === "cancel" ? "cancel" : "default",
574
+ // the tapped action runs its swift closure and clears isPresented,
575
+ // which is the order the system alert uses on the way out: the action
576
+ // body is the last thing that runs with the alert still up. the
577
+ // handlers come off the ref at tap time, not from this render.
578
+ onPress: () => {
579
+ latest.current.onButtonPress?.(index);
580
+ latest.current.onDismiss?.();
581
+ }
582
+ }))
583
+ );
584
+ return () => {
585
+ (0, import_runtime.getNativeAlerts)().dismiss(handle);
586
+ };
587
+ }, []);
588
+ return null;
589
+ }
590
+ function readAlertButtons(value) {
591
+ if (!Array.isArray(value)) return [];
592
+ return value.map((entry) => {
593
+ if (!isRecord(entry)) return {};
594
+ return {
595
+ ...typeof entry.text === "string" ? { text: entry.text } : {},
596
+ ...typeof entry.role === "string" ? { role: entry.role } : {}
597
+ };
598
+ });
599
+ }
352
600
  function nodeProps(node, deliver) {
353
601
  const props = node.p ?? {};
354
602
  switch (node.t) {
@@ -356,7 +604,8 @@ function nodeProps(node, deliver) {
356
604
  const style = fontStyle(fontModifier(node.m));
357
605
  return {
358
606
  children: typeof props.children === "string" ? props.children : "",
359
- ...style ? { style } : {}
607
+ ...style ? { style } : {},
608
+ ...pressProps(node, deliver)
360
609
  };
361
610
  }
362
611
  case "Image": {
@@ -364,14 +613,18 @@ function nodeProps(node, deliver) {
364
613
  const size = typeof font?.size === "number" ? font.size : void 0;
365
614
  return {
366
615
  ...typeof props.systemName === "string" ? { systemName: props.systemName } : {},
367
- ...size === void 0 ? {} : { size }
616
+ ...size === void 0 ? {} : { size },
617
+ ...pressProps(node, deliver)
368
618
  };
369
619
  }
370
620
  case "Toggle": {
371
621
  const onValueChange = handlerId(node, "onValueChange");
372
622
  return {
373
623
  isOn: props.value === true,
374
- ...onValueChange === void 0 ? {} : { onIsOnChange: (value) => deliver(onValueChange, value) }
624
+ ...onValueChange === void 0 ? {} : { onIsOnChange: (value) => deliver(onValueChange, value) },
625
+ // the gesture replaces the row's own press (a label tap runs it and
626
+ // does not toggle); the switch keeps toggling through onValueChange.
627
+ ...pressProps(node, deliver)
375
628
  };
376
629
  }
377
630
  case "TextField": {
@@ -388,17 +641,264 @@ function nodeProps(node, deliver) {
388
641
  value: typeof props.value === "number" ? props.value : 0,
389
642
  min: typeof props.min === "number" ? props.min : 0,
390
643
  max: typeof props.max === "number" ? props.max : 1,
391
- ...onValueChange === void 0 ? {} : { onValueChange: (value) => deliver(onValueChange, value) }
644
+ ...onValueChange === void 0 ? {} : { onValueChange: (value) => deliver(onValueChange, value) },
645
+ // a tap runs the gesture and leaves the value alone: only a drag
646
+ // moves it, so the gesture rides onPress beside the drag handler.
647
+ ...pressProps(node, deliver)
648
+ };
649
+ }
650
+ case "Button":
651
+ return pressProps(node, deliver);
652
+ case "TabView": {
653
+ const onSelect = handlerId(node, "onSelect");
654
+ const tabs = (node.c ?? []).map((tab, index) => {
655
+ const tabProps = tab.p ?? {};
656
+ return {
657
+ ...typeof tabProps.title === "string" ? { title: tabProps.title } : {},
658
+ ...typeof tabProps.systemImage === "string" ? { systemImage: tabProps.systemImage } : {},
659
+ ...typeof tabProps.role === "string" ? { role: tabProps.role } : {},
660
+ testID: `swift-tab-${index}`
661
+ };
662
+ });
663
+ return {
664
+ tabs,
665
+ selection: typeof props.selection === "number" ? props.selection : 0,
666
+ ...onSelect === void 0 ? {} : { onSelect: (index) => deliver(onSelect, index) }
667
+ };
668
+ }
669
+ case "BottomSheet": {
670
+ const onDismiss = handlerId(node, "onDismiss");
671
+ const detents = (node.c ?? []).flatMap((child) => nodeModifiers(child) ?? []).filter((modifier) => modifier.$type === "presentationDetents");
672
+ return {
673
+ isOpened: props.isOpened === true,
674
+ ...detents.length === 0 ? {} : { modifiers: detents },
675
+ ...onDismiss === void 0 ? {} : { onDismiss: () => deliver(onDismiss, null) }
676
+ };
677
+ }
678
+ case "ConfirmationDialog": {
679
+ return { isPresented: props.isPresented === true };
680
+ }
681
+ case "Alert": {
682
+ const onButtonPress = handlerId(node, "onButtonPress");
683
+ const onDismiss = handlerId(node, "onDismiss");
684
+ return {
685
+ title: typeof props.title === "string" ? props.title : "",
686
+ ...typeof props.message === "string" ? { message: props.message } : {},
687
+ buttons: readAlertButtons(props.buttons),
688
+ ...onButtonPress === void 0 ? {} : { onButtonPress: (index) => deliver(onButtonPress, index) },
689
+ ...onDismiss === void 0 ? {} : { onDismiss: () => deliver(onDismiss, null) }
690
+ };
691
+ }
692
+ case "SecureField": {
693
+ const onChangeText = handlerId(node, "onChangeText");
694
+ return {
695
+ ...typeof props.placeholder === "string" ? { placeholder: props.placeholder } : {},
696
+ ...typeof props.prompt === "string" ? { prompt: props.prompt } : {},
697
+ ...typeof props.defaultValue === "string" ? { defaultValue: props.defaultValue } : {},
698
+ ...onChangeText === void 0 ? {} : { onChangeText: (value) => deliver(onChangeText, value) }
392
699
  };
393
700
  }
394
- case "Button": {
395
- const onPress = handlerId(node, "onPress");
396
- return onPress === void 0 ? {} : { onPress: () => deliver(onPress, null) };
701
+ case "Picker": {
702
+ const onSelectionChange = handlerId(node, "onSelectionChange");
703
+ return {
704
+ ...typeof props.title === "string" ? { label: props.title } : {},
705
+ ...props.selection === void 0 ? {} : { selection: props.selection },
706
+ ...onSelectionChange === void 0 ? {} : {
707
+ onSelectionChange: (value) => deliver(onSelectionChange, value)
708
+ }
709
+ };
397
710
  }
711
+ case "DatePicker": {
712
+ return {
713
+ ...typeof props.title === "string" ? { label: props.title } : {},
714
+ // the wire carries epoch seconds; the kit takes a Date and formats it
715
+ ...typeof props.value === "number" ? { value: new Date(props.value * 1e3) } : {},
716
+ ...Array.isArray(props.components) ? { components: props.components } : {}
717
+ };
718
+ }
719
+ case "GlassEffectContainer":
720
+ return {};
721
+ case "ProgressView":
722
+ return {
723
+ ...typeof props.title === "string" ? { label: props.title } : {},
724
+ ...typeof props.value === "number" || props.value === null ? { value: props.value } : {},
725
+ ...typeof props.total === "number" ? { total: props.total } : {}
726
+ };
398
727
  default:
399
- return props;
728
+ return { ...props, ...pressProps(node, deliver) };
400
729
  }
401
730
  }
731
+ function numberProp(node, name) {
732
+ const value = node.p?.[name];
733
+ return typeof value === "number" ? value : 0;
734
+ }
735
+ function menuItemLabel(node) {
736
+ const [only] = node.c ?? [];
737
+ if (node.t !== "Button" || node.c?.length !== 1 || !only) return void 0;
738
+ if (only.t === "Text" && typeof only.p?.children === "string") {
739
+ return { label: only.p.children };
740
+ }
741
+ if (only.t === "Label" && typeof only.p?.title === "string" && (only.p?.systemImage === void 0 || typeof only.p?.systemImage === "string")) {
742
+ return {
743
+ label: only.p.title,
744
+ ...typeof only.p?.systemImage === "string" ? { systemImage: only.p.systemImage } : {}
745
+ };
746
+ }
747
+ return void 0;
748
+ }
749
+ function renderMenuItem(node, deliver, cache) {
750
+ const item = menuItemLabel(node);
751
+ if (item !== void 0) {
752
+ const modifiers = nodeModifiers(node);
753
+ return (0, import_react.createElement)(import_ui.Button, {
754
+ key: node.id,
755
+ ...item,
756
+ ...modifiers === void 0 ? {} : { modifiers },
757
+ ...pressProps(node, deliver)
758
+ });
759
+ }
760
+ return renderNode(node, deliver, cache);
761
+ }
762
+ function renderMenu(node, deliver, cache) {
763
+ const labelCount = numberProp(node, "labelCount");
764
+ const kids = node.c ?? [];
765
+ const labels = kids.slice(0, labelCount);
766
+ const items = kids.slice(labelCount);
767
+ const [only] = labels;
768
+ const onlyText = labels.length === 1 && only.t === "Text" && typeof only.p?.children === "string" ? only.p.children : void 0;
769
+ 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;
770
+ const label = onlyText !== void 0 || onlyLabel !== void 0 ? void 0 : (0, import_react.createElement)(
771
+ import_react.Fragment,
772
+ null,
773
+ ...labels.map((child) => renderNode(child, deliver, cache))
774
+ );
775
+ const labelModifiers = labels.length === 1 ? nodeModifiers(only) : void 0;
776
+ const modifiers = nodeModifiers(node);
777
+ const combined = [...labelModifiers ?? [], ...modifiers ?? []];
778
+ return (0, import_react.createElement)(
779
+ import_ui.Menu,
780
+ {
781
+ key: node.id,
782
+ ...combined.length > 0 ? { modifiers: combined } : {},
783
+ ...onlyText !== void 0 ? { label: onlyText } : {},
784
+ ...onlyLabel !== void 0 ? {
785
+ label: onlyLabel.p?.title,
786
+ ...typeof onlyLabel.p?.systemImage === "string" ? { systemImage: onlyLabel.p.systemImage } : {}
787
+ } : {},
788
+ ...label === void 0 ? {} : { label }
789
+ },
790
+ ...items.map((child) => renderMenuItem(child, deliver))
791
+ );
792
+ }
793
+ function renderContextMenu(node, deliver, cache) {
794
+ const kids = node.c ?? [];
795
+ const contentCount = numberProp(node, "contentCount");
796
+ const itemCount = numberProp(node, "itemCount");
797
+ const content = kids.slice(0, contentCount);
798
+ const items = kids.slice(contentCount, contentCount + itemCount);
799
+ const preview = kids.slice(contentCount + itemCount);
800
+ const modifiers = nodeModifiers(node);
801
+ return (0, import_react.createElement)(
802
+ import_ui.ContextMenu,
803
+ {
804
+ key: node.id,
805
+ ...modifiers === void 0 ? {} : { modifiers }
806
+ },
807
+ (0, import_react.createElement)(
808
+ import_ui.Trigger,
809
+ { key: `${node.id}/trigger` },
810
+ ...content.map((child) => renderNode(child, deliver, cache))
811
+ ),
812
+ (0, import_react.createElement)(
813
+ import_ui.Items,
814
+ { key: `${node.id}/items` },
815
+ ...items.map((child) => renderMenuItem(child, deliver))
816
+ ),
817
+ ...preview.length > 0 ? [
818
+ (0, import_react.createElement)(
819
+ import_ui.Preview,
820
+ { key: `${node.id}/preview` },
821
+ ...preview.map((child) => renderNode(child, deliver, cache))
822
+ )
823
+ ] : []
824
+ );
825
+ }
826
+ function renderSwipeActions(node, deliver, cache) {
827
+ const kids = node.c ?? [];
828
+ const content = kids.slice(0, numberProp(node, "contentCount"));
829
+ const actions = kids.slice(numberProp(node, "contentCount"));
830
+ const modifiers = nodeModifiers(node);
831
+ const { Actions } = import_ui.SwipeActions;
832
+ return (0, import_react.createElement)(
833
+ import_ui.SwipeActions,
834
+ {
835
+ key: node.id,
836
+ ...modifiers === void 0 ? {} : { modifiers }
837
+ },
838
+ ...content.map((child) => renderNode(child, deliver, cache)),
839
+ ...actions.length > 0 ? [
840
+ (0, import_react.createElement)(
841
+ Actions,
842
+ {
843
+ key: `${node.id}/actions`,
844
+ edge: node.p?.edge === "leading" ? "leading" : "trailing",
845
+ ...typeof node.p?.allowsFullSwipe === "boolean" ? { allowsFullSwipe: node.p.allowsFullSwipe } : {}
846
+ },
847
+ ...actions.map((child) => renderNode(child, deliver, cache))
848
+ )
849
+ ] : []
850
+ );
851
+ }
852
+ function ClosureStepper({
853
+ deliver,
854
+ onIncrement,
855
+ onDecrement,
856
+ ...rest
857
+ }) {
858
+ const last = (0, import_react.useRef)(0);
859
+ return (0, import_react.createElement)(import_ui.Stepper, {
860
+ ...rest,
861
+ onValueChange: (next) => {
862
+ if (next > last.current && onIncrement !== void 0) {
863
+ deliver(onIncrement, null);
864
+ } else if (next < last.current && onDecrement !== void 0) {
865
+ deliver(onDecrement, null);
866
+ }
867
+ last.current = next;
868
+ }
869
+ });
870
+ }
871
+ function renderStepper(node, deliver, cache) {
872
+ const kids = node.c ?? [];
873
+ const label = kids.length === 0 ? void 0 : (0, import_react.createElement)(
874
+ import_react.Fragment,
875
+ null,
876
+ ...kids.map((child) => renderNode(child, deliver, cache))
877
+ );
878
+ const modifiers = nodeModifiers(node);
879
+ const shared = {
880
+ key: node.id,
881
+ ...modifiers === void 0 ? {} : { modifiers },
882
+ ...label === void 0 ? {} : { label },
883
+ ...typeof node.p?.min === "number" ? { min: node.p.min } : {},
884
+ ...typeof node.p?.max === "number" ? { max: node.p.max } : {},
885
+ ...typeof node.p?.step === "number" ? { step: node.p.step } : {}
886
+ };
887
+ const onValueChange = handlerId(node, "onValueChange");
888
+ if (onValueChange !== void 0) {
889
+ return (0, import_react.createElement)(import_ui.Stepper, {
890
+ ...shared,
891
+ ...typeof node.p?.value === "number" ? { value: node.p.value } : {},
892
+ onValueChange: (value) => deliver(onValueChange, value)
893
+ });
894
+ }
895
+ return (0, import_react.createElement)(ClosureStepper, {
896
+ ...shared,
897
+ deliver,
898
+ ...handlerId(node, "onIncrement") === void 0 ? {} : { onIncrement: handlerId(node, "onIncrement") },
899
+ ...handlerId(node, "onDecrement") === void 0 ? {} : { onDecrement: handlerId(node, "onDecrement") }
900
+ });
901
+ }
402
902
  function buttonModifier(modifier) {
403
903
  if (modifier.$type === "glassEffect") {
404
904
  const glass = modifier.glass;
@@ -415,11 +915,195 @@ function nodeModifiers(node) {
415
915
  if (node.t !== "Button") return resolved;
416
916
  return resolved?.map(buttonModifier);
417
917
  }
418
- function renderNode(node, deliver) {
918
+ function dialogChildren(node, deliver) {
919
+ const children = node.c?.map((child) => renderNode(child, deliver));
920
+ const props = node.p ?? {};
921
+ if (props.showsTitle !== true || typeof props.title !== "string") return children;
922
+ const title = (0, import_react.createElement)(import_ui.Text, { key: `${node.id}/title`, children: props.title });
923
+ return [title, ...children ?? []];
924
+ }
925
+ var Stack = (0, import_native_stack.createNativeStackNavigator)();
926
+ var NAVIGATOR_FILL = { flex: 1, alignSelf: "stretch" };
927
+ function readNodes(value) {
928
+ if (!Array.isArray(value)) return [];
929
+ return value.map(readNode);
930
+ }
931
+ function hasPrefix(names, of) {
932
+ return names.every((name, index) => of[index] === name);
933
+ }
934
+ function sameNames(a, b) {
935
+ return a.length === b.length && a.every((name, index) => b[index] === name);
936
+ }
937
+ function commonPrefixLength(a, b) {
938
+ let length = 0;
939
+ while (length < a.length && length < b.length && a[length] === b[length]) length++;
940
+ return length;
941
+ }
942
+ function reconcilePlan(names, desired) {
943
+ const common = commonPrefixLength(names, desired);
944
+ if (common === names.length && common === desired.length) return { kind: "in-step" };
945
+ if (common === names.length) return { kind: "push" };
946
+ if (common === desired.length) return { kind: "pop" };
947
+ const top = desired[desired.length - 1];
948
+ return { kind: "replace", replaceAnimation: names.includes(top) ? "pop" : "push" };
949
+ }
950
+ function resolveLargeTitles(modes) {
951
+ let previous = true;
952
+ return modes.map((mode) => {
953
+ const large = mode === "inline" ? false : mode === "large" ? true : previous;
954
+ previous = large;
955
+ return large;
956
+ });
957
+ }
958
+ function screenOptions(node, deliver, largeTitle, replaceAnimation) {
959
+ const props = node.p ?? {};
960
+ const options = {
961
+ title: typeof props.title === "string" ? props.title : "",
962
+ headerLargeTitleEnabled: largeTitle
963
+ };
964
+ if (replaceAnimation !== void 0) options.animationTypeForReplace = replaceAnimation;
965
+ const leading = readNodes(props.leading);
966
+ const trailing = readNodes(props.trailing);
967
+ const principal = readNodes(props.principal);
968
+ const render = (nodes) => () => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_jsx_runtime.Fragment, { children: nodes.map((child) => renderNode(child, deliver)) });
969
+ if (leading.length > 0) {
970
+ options.headerLeft = render(leading);
971
+ options.headerBackVisible = true;
972
+ }
973
+ if (trailing.length > 0) options.headerRight = render(trailing);
974
+ if (principal.length > 0) options.headerTitle = render(principal);
975
+ return options;
976
+ }
977
+ function SwiftScreen({
978
+ node,
979
+ navigation,
980
+ deliver,
981
+ report
982
+ }) {
983
+ const routes = (0, import_native.useNavigationState)(
984
+ (state) => state.routes.map((route) => route.name).join("\0")
985
+ );
986
+ (0, import_react.useEffect)(() => {
987
+ report(navigation, routes === "" ? [] : routes.split("\0"));
988
+ });
989
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_jsx_runtime.Fragment, { children: node.c?.map((child) => renderNode(child, deliver)) });
990
+ }
991
+ function NavigationStackHost({ node, deliver }) {
992
+ const screens = (node.c ?? []).filter((child) => child.t === "NavigationScreen");
993
+ const desired = screens.map((screen) => screen.id);
994
+ const largeTitles = resolveLargeTitles(
995
+ screens.map((screen) => {
996
+ const mode = screen.p?.titleDisplayMode;
997
+ return typeof mode === "string" ? mode : void 0;
998
+ })
999
+ );
1000
+ const onPop = node.h?.onPop;
1001
+ const reportRef = (0, import_react.useRef)(null);
1002
+ const reportedRef = (0, import_react.useRef)([]);
1003
+ const expectedRef = (0, import_react.useRef)(null);
1004
+ const syncedRef = (0, import_react.useRef)(null);
1005
+ const [, rerender] = (0, import_react.useReducer)((count) => count + 1, 0);
1006
+ const report = (0, import_react.useCallback)((navigation, names) => {
1007
+ const previous = reportRef.current;
1008
+ reportRef.current = { navigation, names };
1009
+ if (previous && sameNames(previous.names, names)) return;
1010
+ rerender();
1011
+ }, []);
1012
+ const reported = reportRef.current?.names ?? [];
1013
+ const plan = reportRef.current ? reconcilePlan(reported, desired) : null;
1014
+ const replaceTop = plan?.kind === "replace" ? desired[desired.length - 1] : void 0;
1015
+ (0, import_react.useEffect)(() => {
1016
+ const current = reportRef.current;
1017
+ if (!current) return;
1018
+ const names = current.names;
1019
+ const previous = reportedRef.current;
1020
+ const isReport = !sameNames(previous, names);
1021
+ if (isReport) reportedRef.current = names;
1022
+ const expected = expectedRef.current;
1023
+ if (expected) {
1024
+ if (sameNames(expected, names)) expectedRef.current = null;
1025
+ else if (!isReport) return;
1026
+ else expectedRef.current = null;
1027
+ }
1028
+ const dropped = previous.slice(names.length);
1029
+ const settled = syncedRef.current;
1030
+ const navigatorPopped = isReport && dropped.length > 0 && settled !== null && sameNames(settled, desired) && hasPrefix(names, desired) && hasPrefix(desired.slice(names.length), dropped);
1031
+ if (navigatorPopped && onPop !== void 0) {
1032
+ deliver(onPop, { count: dropped.length });
1033
+ return;
1034
+ }
1035
+ const next = reconcilePlan(names, desired);
1036
+ if (next.kind === "in-step") {
1037
+ expectedRef.current = null;
1038
+ syncedRef.current = desired;
1039
+ return;
1040
+ }
1041
+ expectedRef.current = desired;
1042
+ if (next.kind === "push") {
1043
+ for (const name of desired.slice(names.length)) current.navigation.push(name);
1044
+ return;
1045
+ }
1046
+ if (next.kind === "pop") {
1047
+ current.navigation.pop(names.length - desired.length);
1048
+ return;
1049
+ }
1050
+ current.navigation.reset({
1051
+ index: Math.max(0, desired.length - 1),
1052
+ routes: desired.map((name) => ({ name }))
1053
+ });
1054
+ });
1055
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Stack.Navigator, { initialRouteName: desired[0], children: screens.map((screen, index) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1056
+ Stack.Screen,
1057
+ {
1058
+ name: screen.id,
1059
+ options: screenOptions(
1060
+ screen,
1061
+ deliver,
1062
+ largeTitles[index],
1063
+ screen.id === replaceTop ? plan?.kind === "replace" ? plan.replaceAnimation : void 0 : void 0
1064
+ ),
1065
+ children: (props) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1066
+ SwiftScreen,
1067
+ {
1068
+ node: screen,
1069
+ navigation: props.navigation,
1070
+ deliver,
1071
+ report
1072
+ }
1073
+ )
1074
+ },
1075
+ screen.id
1076
+ )) });
1077
+ }
1078
+ function renderNode(node, deliver, cache) {
1079
+ const cached = cache?.entries.get(node.id);
1080
+ if (cached && sameNode(cached.node, node)) return cached.element;
1081
+ const element = buildNode(node, deliver, cache);
1082
+ cache?.entries.set(node.id, { node, element });
1083
+ return element;
1084
+ }
1085
+ function buildNode(node, deliver, cache) {
1086
+ if (node.t === "Menu") return renderMenu(node, deliver, cache);
1087
+ if (node.t === "ContextMenu") return renderContextMenu(node, deliver, cache);
1088
+ if (node.t === "SwipeActions") return renderSwipeActions(node, deliver, cache);
1089
+ if (node.t === "Stepper") return renderStepper(node, deliver, cache);
1090
+ if (node.t === "NavigationStack") {
1091
+ 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);
1092
+ }
1093
+ if (node.t === "NavigationLink") {
1094
+ return (0, import_react.createElement)(
1095
+ import_ui.Button,
1096
+ {
1097
+ key: node.id,
1098
+ ...pressProps(node, deliver)
1099
+ },
1100
+ ...(node.c ?? []).map((child) => renderNode(child, deliver, cache))
1101
+ );
1102
+ }
419
1103
  const component = NODE_COMPONENTS[node.t];
420
1104
  if (!component) throw new Error(`swift view ${node.t} has no rnx kit component`);
421
1105
  const modifiers = nodeModifiers(node);
422
- const children = node.c?.map((child) => renderNode(child, deliver));
1106
+ const children = node.t === "ConfirmationDialog" ? dialogChildren(node, deliver) : node.c?.map((child) => renderNode(child, deliver));
423
1107
  return (0, import_react.createElement)(
424
1108
  component,
425
1109
  {
@@ -430,6 +1114,25 @@ function renderNode(node, deliver) {
430
1114
  ...children ?? []
431
1115
  );
432
1116
  }
1117
+ function SwiftTree({
1118
+ tree,
1119
+ deliver,
1120
+ cache
1121
+ }) {
1122
+ return (
1123
+ // a navigation stack at the root lays its own bar out under the status
1124
+ // bar and insets each screen itself, so the host must not also pad it
1125
+ // down: that pad would move the whole stack a status bar lower
1126
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1127
+ import_ui.Host,
1128
+ {
1129
+ style: { flex: 1 },
1130
+ ignoreSafeArea: tree.some((node) => node.t === "NavigationStack"),
1131
+ 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)) }) })
1132
+ }
1133
+ )
1134
+ );
1135
+ }
433
1136
  var ROOT_KEY = "__rnx_swift_roots__";
434
1137
  function rootRegistry() {
435
1138
  const existing = Reflect.get(globalThis, ROOT_KEY);
@@ -438,12 +1141,22 @@ function rootRegistry() {
438
1141
  Reflect.set(globalThis, ROOT_KEY, created);
439
1142
  return created;
440
1143
  }
441
- function createRoot() {
1144
+ function slotFor(rootId, mountTree, props) {
1145
+ const registry = rootRegistry();
1146
+ const existing = registry.get(rootId);
1147
+ if (existing) return existing;
1148
+ const created = { root: createRoot(mountTree, props), props };
1149
+ registry.set(rootId, created);
1150
+ return created;
1151
+ }
1152
+ function createRoot(mountTree, props) {
442
1153
  let instance = null;
443
1154
  let source = null;
1155
+ let refresh;
444
1156
  let state = { tree: null, error: null };
445
1157
  let swap = Promise.resolve();
446
1158
  const listeners = /* @__PURE__ */ new Set();
1159
+ const cache = { entries: /* @__PURE__ */ new Map() };
447
1160
  const publish = (next) => {
448
1161
  state = next;
449
1162
  for (const listener of listeners) listener();
@@ -454,7 +1167,7 @@ function createRoot() {
454
1167
  if (carried !== null) created.importState(carried);
455
1168
  if (source !== options) return;
456
1169
  instance = created;
457
- publish({ tree: created.mount(), error: null });
1170
+ publish({ tree: mountTree(created, props.current), error: null });
458
1171
  };
459
1172
  const component = function SwiftRoot() {
460
1173
  const rendered = (0, import_react.useSyncExternalStore)(
@@ -470,14 +1183,15 @@ function createRoot() {
470
1183
  if (!instance) throw new Error("swift app received an event before it mounted");
471
1184
  publish({ tree: instance.deliver(handlerId2, payload), error: null });
472
1185
  };
473
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_ui.Host, { style: { flex: 1 }, children: rendered.tree.map((node) => renderNode(node, deliver)) });
1186
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SwiftTree, { tree: rendered.tree, deliver, cache });
474
1187
  };
475
1188
  return {
476
1189
  component,
477
- use(options) {
478
- const changed = source === null || source.hash !== options.hash || source.url !== options.url;
1190
+ use(options, refreshKey) {
1191
+ const changed = source === null || source.hash !== options.hash || source.url !== options.url || refreshKey !== void 0 && refreshKey !== refresh;
479
1192
  if (changed) {
480
1193
  source = options;
1194
+ refresh = refreshKey;
481
1195
  swap = swap.then(
482
1196
  () => apply(options),
483
1197
  () => apply(options)
@@ -495,13 +1209,41 @@ function createRoot() {
495
1209
  }
496
1210
  function createSwiftModule(options) {
497
1211
  const rootId = options.rootId ?? "root";
498
- const registry = rootRegistry();
499
- const existing = registry.get(rootId);
500
- const root = existing ?? createRoot();
501
- if (!existing) registry.set(rootId, root);
502
- return root.use(options);
1212
+ const slot = slotFor(rootId, (instance) => instance.mount(), { current: {} });
1213
+ return slot.root.use(options);
1214
+ }
1215
+ function propsSignature(props) {
1216
+ try {
1217
+ return JSON.stringify(props, (_key, value) => {
1218
+ if (typeof value === "function" || typeof value === "symbol" || typeof value === "bigint") {
1219
+ throw new Error(`swift view props must be json; found ${typeof value}`);
1220
+ }
1221
+ return value;
1222
+ }) ?? "null";
1223
+ } catch (error) {
1224
+ if (error instanceof Error && error.message.startsWith("swift view props"))
1225
+ throw error;
1226
+ throw new Error(
1227
+ `swift view props must be json: ${error instanceof Error ? error.message : String(error)}`
1228
+ );
1229
+ }
1230
+ }
1231
+ function createSwiftView(options) {
1232
+ const rootId = options.rootId ?? "view";
1233
+ const slot = slotFor(rootId, (instance, props) => instance.mountView(props), {
1234
+ current: {}
1235
+ });
1236
+ return function SwiftView(props) {
1237
+ if (props.children !== void 0) {
1238
+ throw new Error("swift views take data props, not children");
1239
+ }
1240
+ slot.props.current = props;
1241
+ const component = slot.root.use(options, propsSignature(props));
1242
+ return (0, import_react.createElement)(component);
1243
+ };
503
1244
  }
504
1245
  // Annotate the CommonJS export names for ESM import in node:
505
1246
  0 && (module.exports = {
506
- createSwiftModule
1247
+ createSwiftModule,
1248
+ createSwiftView
507
1249
  });