@swiftbrowser/react 0.23.0 → 0.25.0

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.
package/README.md CHANGED
@@ -983,6 +983,43 @@ side and provided again inside:
983
983
  The list must be the same on every render of that component (each entry is
984
984
  read with a hook). Props, callbacks and external stores need no bridging.
985
985
 
986
+ ### Another root's tree, drawn here
987
+
988
+ `RemoteTree` draws a tree that another SwiftUI root produces, wherever that
989
+ root runs: a sandboxed script, a worker, another JavaScript runtime, a
990
+ server. The other side renders with this package as usual, into a root made
991
+ with `remote: true`, and sends the ops it gets to a `RemoteRoot` here; the
992
+ `RemoteRoot` keeps a mirror of the elements and `RemoteTree` draws them as
993
+ elements of this tree, so they lay out, scroll and animate with the rest and
994
+ work on the web and natively alike. Events the renderer sends them go back
995
+ to the other root, synchronously when the other root is in the same thread:
996
+
997
+ ```tsx
998
+ // Here: the host.
999
+ const remote = createRemoteRoot();
1000
+ remote.onEvent((event) => sandbox.dispatch(event)); // taps, text, toggles… for the child's elements
1001
+ remote.onEnvironment((environment) => sandbox.setEnvironment(environment));
1002
+ sandbox.onOps((ops) => remote.applyOps(ops)); // the child's render passes
1003
+ <NavigationStack>
1004
+ <RemoteTree root={remote} />
1005
+ </NavigationStack>
1006
+
1007
+ // There: the child (the same package, its own React).
1008
+ const root = createSwiftUIRoot((ops) => send(ops), { remote: true });
1009
+ root.render(<Counter />);
1010
+ receive((event) => root.dispatch(event));
1011
+ ```
1012
+
1013
+ The child is one screen: its top-level views are drawn in place; what it
1014
+ would put on a screen of its own joins the screen `RemoteTree` sits in
1015
+ (`navigationTitle`, `toolbar`, `searchable`, `toolbarVisibility`), and its
1016
+ `sheet`s and `alert`s are presented over the host's root. A pass the child
1017
+ made in `withAnimation` animates here too, and the child's completion runs
1018
+ when the host's animation ends. Not yet: a `NavigationStack` inside the
1019
+ child (its screens do not become pushes of the host's stack), `Host`
1020
+ elements (their DOM lives in the child's page, which has none here), and
1021
+ the ops a loader answers (`request`, `swiftdata`, `defaults`).
1022
+
986
1023
  ## How it works
987
1024
 
988
1025
  - `src/host/`: a [react-reconciler](https://www.npmjs.com/package/react-reconciler)
@@ -0,0 +1,16 @@
1
+ /**
2
+ * `RemoteTree`: another root's tree drawn here. The child root (made with
3
+ * `createSwiftUIRoot(sink, { remote: true })` wherever it runs) sends its
4
+ * ops to a `RemoteRoot`'s `applyOps`; this component draws the mirror as
5
+ * host elements, so the child's views take part in this tree's layout, its
6
+ * screen and its renderer, web or native alike. What a child hoists to its
7
+ * root for lack of a screen of its own joins this tree's enclosing screen
8
+ * (a title, a toolbar, a search field) or root (a sheet, an alert).
9
+ */
10
+ import { type ReactNode } from 'react';
11
+ import type { RemoteRoot } from '../host/remote';
12
+ export interface RemoteTreeProps {
13
+ root: RemoteRoot;
14
+ }
15
+ export declare function RemoteTree({ root }: RemoteTreeProps): ReactNode;
16
+ //# sourceMappingURL=remoteTree.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"remoteTree.d.ts","sourceRoot":"","sources":["../../src/components/remoteTree.tsx"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAA8E,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAGnH,OAAO,KAAK,EAAc,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAI7D,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,UAAU,CAAC;CAClB;AAYD,wBAAgB,UAAU,CAAC,EAAE,IAAI,EAAE,EAAE,eAAe,GAAG,SAAS,CAyB/D"}
@@ -11,10 +11,20 @@ export type EventType = RendererEvent['type'];
11
11
  export type EventOf<K extends EventType> = Extract<RendererEvent, {
12
12
  type: K;
13
13
  }>;
14
- /** The renderer's events an element answers, by type. */
14
+ /**
15
+ * The renderer's events an element answers, by type. `'*'` takes every
16
+ * event addressed to the element that no typed handler took: a mirrored
17
+ * element (`RemoteTree`) forwards them all to the root that drew it.
18
+ */
15
19
  export type EventHandlers = {
16
20
  [K in EventType]?: (event: EventOf<K>) => void;
21
+ } & {
22
+ '*'?: (event: AddressedEvent) => void;
17
23
  };
24
+ /** An event the renderer addresses to one element. */
25
+ export type AddressedEvent = Extract<RendererEvent, {
26
+ id: number;
27
+ }>;
18
28
  /**
19
29
  * Where a virtual element acts: the nearest enclosing `navscreen` or `sheet`
20
30
  * element, or the root container.
@@ -1 +1 @@
1
- {"version":3,"file":"instance.d.ts","sourceRoot":"","sources":["../../src/host/instance.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,gCAAgC,CAAC;AACpE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAEvC,MAAM,MAAM,SAAS,GAAG,aAAa,CAAC,MAAM,CAAC,CAAC;AAC9C,MAAM,MAAM,OAAO,CAAC,CAAC,SAAS,SAAS,IAAI,OAAO,CAAC,aAAa,EAAE;IAAE,IAAI,EAAE,CAAC,CAAA;CAAE,CAAC,CAAC;AAC/E,yDAAyD;AACzD,MAAM,MAAM,aAAa,GAAG;KAAG,CAAC,IAAI,SAAS,CAAC,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC,CAAC,KAAK,IAAI;CAAE,CAAC;AAE/E;;;GAGG;AACH,MAAM,MAAM,WAAW,GAAG,WAAW,GAAG,OAAO,GAAG,MAAM,CAAC;AAEzD;;;;GAIG;AACH,eAAO,MAAM,SAAS,UAAU,CAAC;AACjC;;;;GAIG;AACH,eAAO,MAAM,UAAU,WAAW,CAAC;AAEnC,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAChC,EAAE,CAAC,EAAE,aAAa,CAAC;IACnB,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,QAAQ,CAAC,EAAE,SAAS,CAAC;CACtB;AAED,MAAM,WAAW,QAAQ;IACvB,0EAA0E;IAC1E,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC/B,EAAE,EAAE,aAAa,GAAG,SAAS,CAAC;IAC9B,MAAM,EAAE,WAAW,GAAG,SAAS,CAAC;IAChC,yEAAyE;IACzE,QAAQ,EAAE,QAAQ,EAAE,CAAC;IACrB,wDAAwD;IACxD,MAAM,EAAE,OAAO,CAAC;IAChB,4DAA4D;IAC5D,OAAO,EAAE,OAAO,CAAC;IACjB,cAAc,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;IAC/C,eAAe,EAAE,QAAQ,GAAG,IAAI,CAAC;IACjC,iBAAiB,EAAE,QAAQ,EAAE,CAAC;CAC/B;AAED,wCAAwC;AACxC,MAAM,WAAW,QAAQ;IACvB,EAAE,EAAE,CAAC,CAAC;IACN,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,QAAQ,EAAE,CAAC;IACrB,iBAAiB,EAAE,QAAQ,EAAE,CAAC;CAC/B;AAED,MAAM,MAAM,QAAQ,GAAG,QAAQ,GAAG,QAAQ,CAAC"}
1
+ {"version":3,"file":"instance.d.ts","sourceRoot":"","sources":["../../src/host/instance.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,gCAAgC,CAAC;AACpE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAEvC,MAAM,MAAM,SAAS,GAAG,aAAa,CAAC,MAAM,CAAC,CAAC;AAC9C,MAAM,MAAM,OAAO,CAAC,CAAC,SAAS,SAAS,IAAI,OAAO,CAAC,aAAa,EAAE;IAAE,IAAI,EAAE,CAAC,CAAA;CAAE,CAAC,CAAC;AAC/E;;;;GAIG;AACH,MAAM,MAAM,aAAa,GAAG;KAAG,CAAC,IAAI,SAAS,CAAC,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC,CAAC,KAAK,IAAI;CAAE,GAAG;IAAE,GAAG,CAAC,EAAE,CAAC,KAAK,EAAE,cAAc,KAAK,IAAI,CAAA;CAAE,CAAC;AAE3H,sDAAsD;AACtD,MAAM,MAAM,cAAc,GAAG,OAAO,CAAC,aAAa,EAAE;IAAE,EAAE,EAAE,MAAM,CAAA;CAAE,CAAC,CAAC;AAEpE;;;GAGG;AACH,MAAM,MAAM,WAAW,GAAG,WAAW,GAAG,OAAO,GAAG,MAAM,CAAC;AAEzD;;;;GAIG;AACH,eAAO,MAAM,SAAS,UAAU,CAAC;AACjC;;;;GAIG;AACH,eAAO,MAAM,UAAU,WAAW,CAAC;AAEnC,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAChC,EAAE,CAAC,EAAE,aAAa,CAAC;IACnB,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,QAAQ,CAAC,EAAE,SAAS,CAAC;CACtB;AAED,MAAM,WAAW,QAAQ;IACvB,0EAA0E;IAC1E,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC/B,EAAE,EAAE,aAAa,GAAG,SAAS,CAAC;IAC9B,MAAM,EAAE,WAAW,GAAG,SAAS,CAAC;IAChC,yEAAyE;IACzE,QAAQ,EAAE,QAAQ,EAAE,CAAC;IACrB,wDAAwD;IACxD,MAAM,EAAE,OAAO,CAAC;IAChB,4DAA4D;IAC5D,OAAO,EAAE,OAAO,CAAC;IACjB,cAAc,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;IAC/C,eAAe,EAAE,QAAQ,GAAG,IAAI,CAAC;IACjC,iBAAiB,EAAE,QAAQ,EAAE,CAAC;CAC/B;AAED,wCAAwC;AACxC,MAAM,WAAW,QAAQ;IACvB,EAAE,EAAE,CAAC,CAAC;IACN,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,QAAQ,EAAE,CAAC;IACrB,iBAAiB,EAAE,QAAQ,EAAE,CAAC;CAC/B;AAED,MAAM,MAAM,QAAQ,GAAG,QAAQ,GAAG,QAAQ,CAAC"}
@@ -0,0 +1,48 @@
1
+ /**
2
+ * A tree drawn by another root. `createRemoteRoot` keeps a mirror of the
3
+ * elements a second SwiftUI root emits (one in a sandbox, a worker, another
4
+ * runtime: anything that produces the ops protocol), and `RemoteTree` draws
5
+ * the mirror as host elements inside a host tree, forwarding the events the
6
+ * renderer sends those elements back to the root that owns them. Everything
7
+ * stays synchronous: a tap mirrored to the child runs its handler, the
8
+ * child's pass re-enters `applyOps`, and the host's own pass draws the result
9
+ * before the tap returns.
10
+ */
11
+ import type { Op, RendererEvent } from '@swiftbrowser/web/src/protocol';
12
+ import type { Environment } from '../environment';
13
+ import type { EventHandlers } from './instance';
14
+ import type { HostTree } from './tree';
15
+ /** An element of the child's tree as the mirror holds it. */
16
+ export interface MirrorNode {
17
+ id: number;
18
+ kind: string;
19
+ props: Record<string, unknown>;
20
+ children: MirrorNode[];
21
+ parent: MirrorNode | null;
22
+ }
23
+ export interface RemoteRoot {
24
+ /** The child root's ops, one pass at a time (each ending in `commit`). */
25
+ applyOps(ops: readonly Op[]): void;
26
+ /** Where the events for the child's elements go: its root's `dispatch`. */
27
+ onEvent(handler: (event: RendererEvent) => void): void;
28
+ /** Where the host's environment goes: its root's `setEnvironment`. */
29
+ onEnvironment(handler: (environment: Environment) => void): void;
30
+ /** An event for the child (by the child's element id, or unaddressed). */
31
+ dispatch(event: RendererEvent): void;
32
+ setEnvironment(environment: Environment): void;
33
+ /** For `useSyncExternalStore`: the version moves on every `commit`. */
34
+ subscribe(listener: () => void): () => void;
35
+ getVersion(): number;
36
+ /** The mirror: element 0, whose children are the child's top-level elements (hoisted sheets and alerts last). */
37
+ readonly root: MirrorNode;
38
+ /** What the child's root carries (a title with no screen of its own, a color scheme); `update` of element 0. */
39
+ readonly rootProps: Record<string, unknown>;
40
+ /** The handlers a mirrored element gets: every event forwarded to the child under the child's id. */
41
+ handlersFor(id: number): EventHandlers;
42
+ /** `RemoteTree` names the tree it draws into, so a child's animated pass animates that tree's. */
43
+ attach(tree: HostTree | null): void;
44
+ /** Forget the mirror and the handlers; the child root is the caller's to unmount. */
45
+ dispose(): void;
46
+ }
47
+ export declare function createRemoteRoot(): RemoteRoot;
48
+ //# sourceMappingURL=remote.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"remote.d.ts","sourceRoot":"","sources":["../../src/host/remote.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAa,EAAE,EAAE,aAAa,EAAE,MAAM,gCAAgC,CAAC;AACnF,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAClD,OAAO,KAAK,EAAkB,aAAa,EAAE,MAAM,YAAY,CAAC;AAEhE,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,QAAQ,CAAC;AAEvC,6DAA6D;AAC7D,MAAM,WAAW,UAAU;IACzB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC/B,QAAQ,EAAE,UAAU,EAAE,CAAC;IACvB,MAAM,EAAE,UAAU,GAAG,IAAI,CAAC;CAC3B;AAED,MAAM,WAAW,UAAU;IACzB,0EAA0E;IAC1E,QAAQ,CAAC,GAAG,EAAE,SAAS,EAAE,EAAE,GAAG,IAAI,CAAC;IACnC,2EAA2E;IAC3E,OAAO,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE,aAAa,KAAK,IAAI,GAAG,IAAI,CAAC;IACvD,sEAAsE;IACtE,aAAa,CAAC,OAAO,EAAE,CAAC,WAAW,EAAE,WAAW,KAAK,IAAI,GAAG,IAAI,CAAC;IACjE,0EAA0E;IAC1E,QAAQ,CAAC,KAAK,EAAE,aAAa,GAAG,IAAI,CAAC;IACrC,cAAc,CAAC,WAAW,EAAE,WAAW,GAAG,IAAI,CAAC;IAC/C,uEAAuE;IACvE,SAAS,CAAC,QAAQ,EAAE,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC;IAC5C,UAAU,IAAI,MAAM,CAAC;IACrB,iHAAiH;IACjH,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAC;IAC1B,gHAAgH;IAChH,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC5C,qGAAqG;IACrG,WAAW,CAAC,EAAE,EAAE,MAAM,GAAG,aAAa,CAAC;IACvC,kGAAkG;IAClG,MAAM,CAAC,IAAI,EAAE,QAAQ,GAAG,IAAI,GAAG,IAAI,CAAC;IACpC,qFAAqF;IACrF,OAAO,IAAI,IAAI,CAAC;CACjB;AAED,wBAAgB,gBAAgB,IAAI,UAAU,CA+I7C"}
@@ -7,7 +7,7 @@
7
7
  import type { Animation, ColorScheme, DynamicTypeSize, Op, RendererEvent } from '@swiftbrowser/web/src/protocol';
8
8
  import { type ReactNode } from 'react';
9
9
  import { type Environment } from '../environment';
10
- import { type OpsSink } from './tree';
10
+ import { HostTree, type HostTreeOptions, type OpsSink } from './tree';
11
11
  export interface SwiftUIRoot {
12
12
  /** Render (or re-render) the tree; the ops of the pass reach the sink before this returns. */
13
13
  render(element: ReactNode): void;
@@ -17,7 +17,14 @@ export interface SwiftUIRoot {
17
17
  /** The device environment the tree reads through `useColorScheme` and friends. */
18
18
  setEnvironment(environment: Partial<Environment>): void;
19
19
  }
20
- export declare function createSwiftUIRoot(sink: OpsSink): SwiftUIRoot;
20
+ /** The tree a component renders into (`RemoteTree` stamps a child's animation on it). */
21
+ export declare const HostTreeContext: import("react").Context<HostTree | null>;
22
+ export type SwiftUIRootOptions = HostTreeOptions;
23
+ /**
24
+ * A root whose ops reach `sink`. `remote: true` for a tree another root
25
+ * draws through `RemoteTree` (`createRemoteRoot`), rather than a renderer.
26
+ */
27
+ export declare function createSwiftUIRoot(sink: OpsSink, options?: SwiftUIRootOptions): SwiftUIRoot;
21
28
  export type { ColorScheme, DynamicTypeSize, Op };
22
29
  /**
23
30
  * Registers the reconciler with the React DevTools hook (`__REACT_DEVTOOLS_GLOBAL_HOOK__`)
@@ -36,6 +43,17 @@ export interface WithAnimationOptions {
36
43
  */
37
44
  completionCriteria?: AnimationCompletionCriteria;
38
45
  }
46
+ /**
47
+ * Stamps one tree's next `commit` with an animation (and a completion, whose
48
+ * serial is answered), without a body: for a pass another root animated,
49
+ * drawn here by `RemoteTree`. The stamp waits for the tree's next flush,
50
+ * however late React runs it; `withAnimation` clears its stamps as its body
51
+ * returns.
52
+ */
53
+ export declare function stampNextCommit(tree: HostTree, animation: Animation, completion?: {
54
+ criteria: AnimationCompletionCriteria;
55
+ callback: () => void;
56
+ }): number;
39
57
  /**
40
58
  * `withAnimation { }`: the state changes `body` makes render in one pass whose
41
59
  * `commit` carries the animation, so the renderer animates every frame and
@@ -1 +1 @@
1
- {"version":3,"file":"root.d.ts","sourceRoot":"","sources":["../../src/host/root.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,WAAW,EAAE,eAAe,EAAE,EAAE,EAAE,aAAa,EAAE,MAAM,gCAAgC,CAAC;AACjH,OAAO,EAAiB,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAEtD,OAAO,EAAuB,KAAK,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAEvE,OAAO,EAAY,KAAK,OAAO,EAAE,MAAM,QAAQ,CAAC;AAEhD,MAAM,WAAW,WAAW;IAC1B,8FAA8F;IAC9F,MAAM,CAAC,OAAO,EAAE,SAAS,GAAG,IAAI,CAAC;IACjC,OAAO,IAAI,IAAI,CAAC;IAChB,oEAAoE;IACpE,QAAQ,CAAC,KAAK,EAAE,aAAa,GAAG,IAAI,CAAC;IACrC,kFAAkF;IAClF,cAAc,CAAC,WAAW,EAAE,OAAO,CAAC,WAAW,CAAC,GAAG,IAAI,CAAC;CACzD;AAMD,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,OAAO,GAAG,WAAW,CAgD5D;AAED,YAAY,EAAE,WAAW,EAAE,eAAe,EAAE,EAAE,EAAE,CAAC;AAEjD;;;;;GAKG;AACH,wBAAgB,kBAAkB,IAAI,OAAO,CAE5C;AAED,6EAA6E;AAC7E,MAAM,MAAM,2BAA2B,GAAG,mBAAmB,GAAG,SAAS,CAAC;AAE1E,MAAM,WAAW,oBAAoB;IACnC;;;;OAIG;IACH,kBAAkB,CAAC,EAAE,2BAA2B,CAAC;CAClD;AAID;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAAC,CAAC,EAAE,SAAS,EAAE,SAAS,GAAG,SAAS,EAAE,IAAI,EAAE,MAAM,CAAC,EAAE,UAAU,CAAC,EAAE,MAAM,IAAI,GAAG,CAAC,CAAC;AAC9G,wBAAgB,aAAa,CAAC,CAAC,EAAE,SAAS,EAAE,SAAS,GAAG,SAAS,EAAE,OAAO,EAAE,oBAAoB,EAAE,IAAI,EAAE,MAAM,CAAC,EAAE,UAAU,CAAC,EAAE,MAAM,IAAI,GAAG,CAAC,CAAC;AAC7I,wBAAgB,aAAa,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,CAAC,GAAG,CAAC,CAAC"}
1
+ {"version":3,"file":"root.d.ts","sourceRoot":"","sources":["../../src/host/root.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,WAAW,EAAE,eAAe,EAAE,EAAE,EAAE,aAAa,EAAE,MAAM,gCAAgC,CAAC;AACjH,OAAO,EAAgC,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAErE,OAAO,EAAuB,KAAK,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAEvE,OAAO,EAAE,QAAQ,EAAE,KAAK,eAAe,EAAE,KAAK,OAAO,EAAE,MAAM,QAAQ,CAAC;AAEtE,MAAM,WAAW,WAAW;IAC1B,8FAA8F;IAC9F,MAAM,CAAC,OAAO,EAAE,SAAS,GAAG,IAAI,CAAC;IACjC,OAAO,IAAI,IAAI,CAAC;IAChB,oEAAoE;IACpE,QAAQ,CAAC,KAAK,EAAE,aAAa,GAAG,IAAI,CAAC;IACrC,kFAAkF;IAClF,cAAc,CAAC,WAAW,EAAE,OAAO,CAAC,WAAW,CAAC,GAAG,IAAI,CAAC;CACzD;AAMD,yFAAyF;AACzF,eAAO,MAAM,eAAe,0CAAuC,CAAC;AAEpE,MAAM,MAAM,kBAAkB,GAAG,eAAe,CAAC;AAEjD;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,OAAO,EAAE,OAAO,GAAE,kBAAuB,GAAG,WAAW,CAgD9F;AAED,YAAY,EAAE,WAAW,EAAE,eAAe,EAAE,EAAE,EAAE,CAAC;AAEjD;;;;;GAKG;AACH,wBAAgB,kBAAkB,IAAI,OAAO,CAE5C;AAED,6EAA6E;AAC7E,MAAM,MAAM,2BAA2B,GAAG,mBAAmB,GAAG,SAAS,CAAC;AAE1E,MAAM,WAAW,oBAAoB;IACnC;;;;OAIG;IACH,kBAAkB,CAAC,EAAE,2BAA2B,CAAC;CAClD;AAID;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,QAAQ,EAAE,SAAS,EAAE,SAAS,EAAE,UAAU,CAAC,EAAE;IAAE,QAAQ,EAAE,2BAA2B,CAAC;IAAC,QAAQ,EAAE,MAAM,IAAI,CAAA;CAAE,GAAG,MAAM,CAM1J;AAED;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAAC,CAAC,EAAE,SAAS,EAAE,SAAS,GAAG,SAAS,EAAE,IAAI,EAAE,MAAM,CAAC,EAAE,UAAU,CAAC,EAAE,MAAM,IAAI,GAAG,CAAC,CAAC;AAC9G,wBAAgB,aAAa,CAAC,CAAC,EAAE,SAAS,EAAE,SAAS,GAAG,SAAS,EAAE,OAAO,EAAE,oBAAoB,EAAE,IAAI,EAAE,MAAM,CAAC,EAAE,UAAU,CAAC,EAAE,MAAM,IAAI,GAAG,CAAC,CAAC;AAC7I,wBAAgB,aAAa,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,CAAC,GAAG,CAAC,CAAC"}
@@ -8,8 +8,19 @@
8
8
  import type { Animation, Op, RendererEvent } from '@swiftbrowser/web/src/protocol';
9
9
  import { type HostProps, type Instance, type RootNode } from './instance';
10
10
  export type OpsSink = (ops: Op[]) => void;
11
+ export interface HostTreeOptions {
12
+ /**
13
+ * The tree is drawn by another root (`RemoteTree`), not a renderer: a
14
+ * preference with no screen to take it goes to the root instead of being
15
+ * dropped, and the root's props are sent whole as `update` of element 0
16
+ * (a renderer takes only `colorScheme` there), so the host can lift them
17
+ * to the screen it draws the tree in.
18
+ */
19
+ remote?: boolean;
20
+ }
11
21
  export declare class HostTree {
12
22
  private readonly sink;
23
+ private readonly options;
13
24
  readonly root: RootNode;
14
25
  private nextId;
15
26
  /** Emitted elements by id, for event dispatch. */
@@ -24,11 +35,14 @@ export declare class HostTree {
24
35
  serial: number;
25
36
  criteria: 'logicallyComplete' | 'removed';
26
37
  callback: () => void;
38
+ whenNothingDrawn?: boolean;
27
39
  } | undefined;
28
40
  private readonly completions;
29
41
  /** The root's `colorScheme` last sent (`.preferredColorScheme`); undefined while none was. */
30
42
  private committedColorScheme;
31
- constructor(sink: OpsSink);
43
+ /** Remote: the root's props last sent. */
44
+ private committedRootProps;
45
+ constructor(sink: OpsSink, options?: HostTreeOptions);
32
46
  createInstance(props: HostProps): Instance;
33
47
  updateInstance(instance: Instance, next: HostProps): void;
34
48
  /** Emit the ops for everything that changed since the last commit. */
@@ -1 +1 @@
1
- {"version":3,"file":"tree.d.ts","sourceRoot":"","sources":["../../src/host/tree.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAY,EAAE,EAAE,aAAa,EAAE,MAAM,gCAAgC,CAAC;AAC7F,OAAO,EAAyB,KAAK,SAAS,EAAE,KAAK,QAAQ,EAAE,KAAK,QAAQ,EAAiB,MAAM,YAAY,CAAC;AAEhH,MAAM,MAAM,OAAO,GAAG,CAAC,GAAG,EAAE,EAAE,EAAE,KAAK,IAAI,CAAC;AAqC1C,qBAAa,QAAQ;IAgBP,OAAO,CAAC,QAAQ,CAAC,IAAI;IAfjC,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAgE;IACvF,OAAO,CAAC,MAAM,CAAK;IACnB,kDAAkD;IAClD,OAAO,CAAC,QAAQ,CAAC,IAAI,CAA+B;IACpD,OAAO,CAAC,aAAa,CAAuB;IAC5C,4EAA4E;IAC5E,OAAO,CAAC,OAAO,CAAgB;IAC/B,qDAAqD;IACrD,gBAAgB,EAAE,SAAS,GAAG,SAAS,CAAC;IACxC,8IAA8I;IAC9I,iBAAiB,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,mBAAmB,GAAG,SAAS,CAAC;QAAC,QAAQ,EAAE,MAAM,IAAI,CAAA;KAAE,GAAG,SAAS,CAAC;IACnH,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAiC;IAC7D,8FAA8F;IAC9F,OAAO,CAAC,oBAAoB,CAAsC;gBAErC,IAAI,EAAE,OAAO;IAE1C,cAAc,CAAC,KAAK,EAAE,SAAS,GAAG,QAAQ;IAgB1C,cAAc,CAAC,QAAQ,EAAE,QAAQ,EAAE,IAAI,EAAE,SAAS,GAAG,IAAI;IAoBzD,sEAAsE;IACtE,MAAM,IAAI,IAAI;IAKd,KAAK,IAAI,EAAE,EAAE;IAwHb,qEAAqE;IACrE,gBAAgB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO;IAIzC,mEAAmE;IACnE,QAAQ,CAAC,KAAK,EAAE,aAAa,GAAG,IAAI;CAgBrC;AAED,+CAA+C;AAC/C,wBAAgB,SAAS,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,OAAO,GAAG,OAAO,CAazD"}
1
+ {"version":3,"file":"tree.d.ts","sourceRoot":"","sources":["../../src/host/tree.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAY,EAAE,EAAE,aAAa,EAAE,MAAM,gCAAgC,CAAC;AAC7F,OAAO,EAAyB,KAAK,SAAS,EAAE,KAAK,QAAQ,EAAE,KAAK,QAAQ,EAAiB,MAAM,YAAY,CAAC;AAEhH,MAAM,MAAM,OAAO,GAAG,CAAC,GAAG,EAAE,EAAE,EAAE,KAAK,IAAI,CAAC;AAE1C,MAAM,WAAW,eAAe;IAC9B;;;;;;OAMG;IACH,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAqCD,qBAAa,QAAQ;IAmBjB,OAAO,CAAC,QAAQ,CAAC,IAAI;IACrB,OAAO,CAAC,QAAQ,CAAC,OAAO;IAnB1B,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAgE;IACvF,OAAO,CAAC,MAAM,CAAK;IACnB,kDAAkD;IAClD,OAAO,CAAC,QAAQ,CAAC,IAAI,CAA+B;IACpD,OAAO,CAAC,aAAa,CAAuB;IAC5C,4EAA4E;IAC5E,OAAO,CAAC,OAAO,CAAgB;IAC/B,qDAAqD;IACrD,gBAAgB,EAAE,SAAS,GAAG,SAAS,CAAC;IACxC,8IAA8I;IAC9I,iBAAiB,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,mBAAmB,GAAG,SAAS,CAAC;QAAC,QAAQ,EAAE,MAAM,IAAI,CAAC;QAAC,gBAAgB,CAAC,EAAE,OAAO,CAAA;KAAE,GAAG,SAAS,CAAC;IAC/I,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAiC;IAC7D,8FAA8F;IAC9F,OAAO,CAAC,oBAAoB,CAAsC;IAClE,0CAA0C;IAC1C,OAAO,CAAC,kBAAkB,CAA+B;gBAGtC,IAAI,EAAE,OAAO,EACb,OAAO,GAAE,eAAoB;IAGhD,cAAc,CAAC,KAAK,EAAE,SAAS,GAAG,QAAQ;IAgB1C,cAAc,CAAC,QAAQ,EAAE,QAAQ,EAAE,IAAI,EAAE,SAAS,GAAG,IAAI;IAoBzD,sEAAsE;IACtE,MAAM,IAAI,IAAI;IAKd,KAAK,IAAI,EAAE,EAAE;IAoIb,qEAAqE;IACrE,gBAAgB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO;IAIzC,mEAAmE;IACnE,QAAQ,CAAC,KAAK,EAAE,aAAa,GAAG,IAAI;CAgBrC;AAED,+CAA+C;AAC/C,wBAAgB,SAAS,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,OAAO,GAAG,OAAO,CAazD"}
package/dist/index.d.ts CHANGED
@@ -3,7 +3,10 @@
3
3
  * SwiftBrowser's render ops; `createSwiftUIRoot` hands them to the renderer
4
4
  * of @swiftbrowser/web, which draws them with its iOS layout engine.
5
5
  */
6
- export { createSwiftUIRoot, injectIntoDevTools, withAnimation, type AnimationCompletionCriteria, type SwiftUIRoot, type WithAnimationOptions } from './host/root';
6
+ export { createSwiftUIRoot, injectIntoDevTools, withAnimation, type AnimationCompletionCriteria, type SwiftUIRoot, type SwiftUIRootOptions, type WithAnimationOptions } from './host/root';
7
+ export { createRemoteRoot, type MirrorNode, type RemoteRoot } from './host/remote';
8
+ export { RemoteTree, type RemoteTreeProps } from './components/remoteTree';
9
+ export type { Op, RendererEvent } from '@swiftbrowser/web/src/protocol';
7
10
  export { mount, type MountOptions, type Mounted } from './mount';
8
11
  export { SwiftUIView, type SwiftUIViewProps } from './island';
9
12
  export { useContextBridge } from './bridge';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import { createContext, useContext, createElement, useSyncExternalStore, useRef, useLayoutEffect, useState, Children, useEffect, isValidElement, Fragment, useId, cloneElement, useCallback } from "react";
1
+ import { createContext, useContext, createElement, useSyncExternalStore, useLayoutEffect, Fragment, useRef, useState, Children, useEffect, isValidElement, useId, cloneElement, useCallback } from "react";
2
2
  import { DefaultEventPriority, ConcurrentRoot } from "react-reconciler/constants";
3
3
  import Reconciler from "react-reconciler";
4
4
  import { horizontalSizeClassFor } from "@swiftbrowser/web/src/protocol";
@@ -207,10 +207,12 @@ function inheritTitleModes(screens, effective) {
207
207
  }
208
208
  }
209
209
  class HostTree {
210
- constructor(sink) {
210
+ constructor(sink, options = {}) {
211
211
  this.sink = sink;
212
+ this.options = options;
212
213
  }
213
214
  sink;
215
+ options;
214
216
  root = { id: 0, kind: "root", children: [], committedChildren: [] };
215
217
  nextId = 1;
216
218
  /** Emitted elements by id, for event dispatch. */
@@ -225,6 +227,8 @@ class HostTree {
225
227
  completions = /* @__PURE__ */ new Map();
226
228
  /** The root's `colorScheme` last sent (`.preferredColorScheme`); undefined while none was. */
227
229
  committedColorScheme;
230
+ /** Remote: the root's props last sent. */
231
+ committedRootProps = {};
228
232
  createInstance(props) {
229
233
  return {
230
234
  id: this.nextId++,
@@ -273,7 +277,7 @@ class HostTree {
273
277
  const visit = (child, sink, ctx) => {
274
278
  if (child.hidden) return;
275
279
  if (child.kind === PREF_KIND) {
276
- const target = child.target === "root" || child.target === void 0 ? this.root : ctx[child.target];
280
+ const target = child.target === "root" || child.target === void 0 ? this.root : ctx[child.target] ?? (this.options.remote ? this.root : void 0);
277
281
  const entry2 = target ? effective.get(target) : void 0;
278
282
  if (entry2) Object.assign(entry2.props, child.props);
279
283
  return;
@@ -287,8 +291,8 @@ class HostTree {
287
291
  const entry = { props: { ...child.props }, children: [], hoisted: [] };
288
292
  effective.set(child, entry);
289
293
  sink.push(child);
290
- const next2 = child.kind === "navscreen" ? { ...ctx, navscreen: child } : child.kind === "sheet" ? { ...ctx, sheet: child } : ctx;
291
- for (const grandchild of child.children) visit(grandchild, entry.children, next2);
294
+ const next = child.kind === "navscreen" ? { ...ctx, navscreen: child } : child.kind === "sheet" ? { ...ctx, sheet: child } : ctx;
295
+ for (const grandchild of child.children) visit(grandchild, entry.children, next);
292
296
  };
293
297
  for (const child of this.root.children) visit(child, rootEffective.children, {});
294
298
  for (const entry of effective.values()) {
@@ -347,13 +351,23 @@ class HostTree {
347
351
  for (const id of this.retired) ops.push({ op: "remove", id });
348
352
  this.retired = [];
349
353
  this.committedLive = live;
350
- const scheme = rootEffective.props["colorScheme"];
351
- const next = scheme === void 0 ? this.committedColorScheme === void 0 ? void 0 : null : scheme;
352
- if (next !== void 0 && next !== this.committedColorScheme) {
353
- ops.push({ op: "update", id: 0, props: { colorScheme: next } });
354
- this.committedColorScheme = next === null ? void 0 : next;
354
+ if (this.options.remote) {
355
+ if (!deepEqual(rootEffective.props, this.committedRootProps)) {
356
+ ops.push({ op: "update", id: 0, props: rootEffective.props });
357
+ this.committedRootProps = rootEffective.props;
358
+ }
359
+ } else {
360
+ const scheme = rootEffective.props["colorScheme"];
361
+ const next = scheme === void 0 ? this.committedColorScheme === void 0 ? void 0 : null : scheme;
362
+ if (next !== void 0 && next !== this.committedColorScheme) {
363
+ ops.push({ op: "update", id: 0, props: { colorScheme: next } });
364
+ this.committedColorScheme = next === null ? void 0 : next;
365
+ }
366
+ }
367
+ if (ops.length === 0) {
368
+ if (completion?.whenNothingDrawn) completion.callback();
369
+ return ops;
355
370
  }
356
- if (ops.length === 0) return ops;
357
371
  const commit = { op: "commit" };
358
372
  if (animation) commit.animation = animation;
359
373
  if (completion) {
@@ -382,7 +396,7 @@ class HostTree {
382
396
  }
383
397
  if (!("id" in event)) return;
384
398
  const instance = this.live.get(event.id);
385
- const handler = instance?.on?.[event.type];
399
+ const handler = instance?.on?.[event.type] ?? instance?.on?.["*"];
386
400
  handler?.(event);
387
401
  }
388
402
  }
@@ -402,8 +416,9 @@ function deepEqual(a, b) {
402
416
  }
403
417
  const DEFAULT_ENVIRONMENT = { colorScheme: "light", dynamicTypeSize: "large", horizontalSizeClass: "compact", scenePhase: "active" };
404
418
  const roots = /* @__PURE__ */ new Set();
405
- function createSwiftUIRoot(sink) {
406
- const tree = new HostTree(sink);
419
+ const HostTreeContext = createContext(null);
420
+ function createSwiftUIRoot(sink, options = {}) {
421
+ const tree = new HostTree(sink, options);
407
422
  roots.add(tree);
408
423
  const container = reconciler.createContainer(
409
424
  tree,
@@ -424,7 +439,7 @@ function createSwiftUIRoot(sink) {
424
439
  let environment = DEFAULT_ENVIRONMENT;
425
440
  let current = null;
426
441
  const update = () => {
427
- reconciler.updateContainerSync(createElement(EnvironmentProvider, { value: environment }, current), container, null, null);
442
+ reconciler.updateContainerSync(createElement(HostTreeContext.Provider, { value: tree }, createElement(EnvironmentProvider, { value: environment }, current)), container, null, null);
428
443
  reconciler.flushSyncWork();
429
444
  };
430
445
  return {
@@ -450,6 +465,13 @@ function injectIntoDevTools() {
450
465
  return reconciler.injectIntoDevTools() === true;
451
466
  }
452
467
  let nextCompletionSerial = 1;
468
+ function stampNextCommit(tree, animation, completion) {
469
+ tree.pendingAnimation = animation;
470
+ if (!completion) return 0;
471
+ const serial = nextCompletionSerial++;
472
+ tree.pendingCompletion = { serial, criteria: completion.criteria, callback: completion.callback, whenNothingDrawn: true };
473
+ return serial;
474
+ }
453
475
  function withAnimation(first, second, third, fourth) {
454
476
  const animation = typeof first === "function" ? { type: "default" } : first ?? { type: "default" };
455
477
  let body;
@@ -482,6 +504,176 @@ function withAnimation(first, second, third, fourth) {
482
504
  if (completion && !committed) completion();
483
505
  }
484
506
  }
507
+ function createRemoteRoot() {
508
+ const root = { id: 0, kind: "root", props: {}, children: [], parent: null };
509
+ const nodes = /* @__PURE__ */ new Map([[0, root]]);
510
+ const handlers = /* @__PURE__ */ new Map();
511
+ const listeners = /* @__PURE__ */ new Set();
512
+ let version = 0;
513
+ let rootProps = {};
514
+ let eventHandler;
515
+ let environmentHandler;
516
+ let lastEnvironment;
517
+ let hostTree = null;
518
+ const detach2 = (node) => {
519
+ if (!node.parent) return;
520
+ const siblings = node.parent.children;
521
+ const at = siblings.indexOf(node);
522
+ if (at >= 0) siblings.splice(at, 1);
523
+ node.parent = null;
524
+ };
525
+ const forget = (node) => {
526
+ nodes.delete(node.id);
527
+ handlers.delete(node.id);
528
+ for (const child of node.children) forget(child);
529
+ };
530
+ const notify2 = () => {
531
+ version += 1;
532
+ for (const listener of listeners) listener();
533
+ };
534
+ const remote = {
535
+ applyOps(ops) {
536
+ let commit = null;
537
+ for (const op of ops) {
538
+ switch (op.op) {
539
+ case "create": {
540
+ if (nodes.has(op.id)) throw new Error(`remote tree: element ${op.id} created twice`);
541
+ nodes.set(op.id, { id: op.id, kind: op.kind, props: op.props, children: [], parent: null });
542
+ break;
543
+ }
544
+ case "update": {
545
+ if (op.id === 0) {
546
+ rootProps = op.props;
547
+ break;
548
+ }
549
+ const node = nodes.get(op.id);
550
+ if (!node) throw new Error(`remote tree: update of unknown element ${op.id}`);
551
+ node.props = op.props;
552
+ break;
553
+ }
554
+ case "insert": {
555
+ const node = nodes.get(op.id);
556
+ const parent = nodes.get(op.parent);
557
+ if (!node || !parent) throw new Error(`remote tree: insert of ${op.id} into ${op.parent}: unknown element`);
558
+ detach2(node);
559
+ parent.children.splice(op.index, 0, node);
560
+ node.parent = parent;
561
+ break;
562
+ }
563
+ case "remove": {
564
+ const node = nodes.get(op.id);
565
+ if (!node) break;
566
+ detach2(node);
567
+ forget(node);
568
+ break;
569
+ }
570
+ case "commit":
571
+ commit = op;
572
+ break;
573
+ }
574
+ }
575
+ if (!commit) return;
576
+ const { animation, completion, completionCriteria } = commit;
577
+ const answer = completion === void 0 ? void 0 : () => remote.dispatch({ type: "animationcomplete", serial: completion });
578
+ if (animation && hostTree) {
579
+ stampNextCommit(hostTree, animation, answer && { criteria: completionCriteria ?? "logicallyComplete", callback: answer });
580
+ notify2();
581
+ return;
582
+ }
583
+ notify2();
584
+ answer?.();
585
+ },
586
+ onEvent(handler) {
587
+ eventHandler = handler;
588
+ },
589
+ onEnvironment(handler) {
590
+ environmentHandler = handler;
591
+ if (lastEnvironment) handler(lastEnvironment);
592
+ },
593
+ dispatch(event) {
594
+ eventHandler?.(event);
595
+ },
596
+ setEnvironment(environment) {
597
+ if (lastEnvironment && lastEnvironment.colorScheme === environment.colorScheme && lastEnvironment.dynamicTypeSize === environment.dynamicTypeSize && lastEnvironment.horizontalSizeClass === environment.horizontalSizeClass && lastEnvironment.scenePhase === environment.scenePhase)
598
+ return;
599
+ lastEnvironment = environment;
600
+ environmentHandler?.(environment);
601
+ },
602
+ subscribe(listener) {
603
+ listeners.add(listener);
604
+ return () => {
605
+ listeners.delete(listener);
606
+ };
607
+ },
608
+ getVersion: () => version,
609
+ get root() {
610
+ return root;
611
+ },
612
+ get rootProps() {
613
+ return rootProps;
614
+ },
615
+ attach(tree) {
616
+ hostTree = tree;
617
+ },
618
+ handlersFor(id) {
619
+ let on = handlers.get(id);
620
+ if (!on) {
621
+ on = { "*": (event) => remote.dispatch({ ...event, id }) };
622
+ handlers.set(id, on);
623
+ }
624
+ return on;
625
+ },
626
+ dispose() {
627
+ root.children = [];
628
+ nodes.clear();
629
+ nodes.set(0, root);
630
+ handlers.clear();
631
+ listeners.clear();
632
+ eventHandler = void 0;
633
+ environmentHandler = void 0;
634
+ hostTree = null;
635
+ }
636
+ };
637
+ return remote;
638
+ }
639
+ function sb(kind, props, on, children, target) {
640
+ const hostProps = { kind };
641
+ if (props) hostProps.props = props;
642
+ if (on) hostProps.on = on;
643
+ if (target) hostProps.target = target;
644
+ return children === void 0 ? createElement("sb", hostProps) : createElement("sb", hostProps, children);
645
+ }
646
+ const SCREEN_PROPS = ["title", "displayMode", "toolbarRole", "navigationBarHidden", "bottomBarHidden", "statusBarHidden"];
647
+ function renderNode(root, node) {
648
+ if (node.kind === "host") return null;
649
+ const children = node.children.length ? node.children.map((child) => renderNode(root, child)) : void 0;
650
+ return createElement(Fragment, { key: node.id }, sb(node.kind, node.props, root.handlersFor(node.id), children));
651
+ }
652
+ function RemoteTree({ root }) {
653
+ useSyncExternalStore(root.subscribe, root.getVersion, root.getVersion);
654
+ const environment = useContext(EnvironmentContext);
655
+ const tree = useContext(HostTreeContext);
656
+ useLayoutEffect(() => {
657
+ root.attach(tree);
658
+ return () => root.attach(null);
659
+ }, [root, tree]);
660
+ useLayoutEffect(() => {
661
+ root.setEnvironment(environment);
662
+ }, [root, environment]);
663
+ const out = [];
664
+ const screen = {};
665
+ for (const key of SCREEN_PROPS) if (key in root.rootProps) screen[key] = root.rootProps[key];
666
+ if (Object.keys(screen).length) out.push(createElement(Fragment, { key: "$screen" }, sb(PREF_KIND, screen, void 0, void 0, "navscreen")));
667
+ if ("colorScheme" in root.rootProps)
668
+ out.push(createElement(Fragment, { key: "$scheme" }, sb(PREF_KIND, { colorScheme: root.rootProps["colorScheme"] }, void 0, void 0, "root")));
669
+ for (const child of root.root.children) {
670
+ const drawn = renderNode(root, child);
671
+ if (child.kind === "sheet" || child.kind === "alert") out.push(createElement(Fragment, { key: child.id }, sb(HOIST_KIND, void 0, void 0, drawn, "root")));
672
+ else if (child.kind === "toolbar" || child.kind === "searchfield") out.push(createElement(Fragment, { key: child.id }, sb(HOIST_KIND, void 0, void 0, drawn, "navscreen")));
673
+ else out.push(drawn);
674
+ }
675
+ return createElement(Fragment, null, ...out);
676
+ }
485
677
  const HIERARCHY = { primary: 1, secondary: 2, tertiary: 3, quaternary: 4 };
486
678
  function rgb(r2, g, b, a = 1) {
487
679
  return { r: r2, g, b, a };
@@ -1021,13 +1213,6 @@ function readBinding(value, onChange) {
1021
1213
  return [value, onChange ?? (() => {
1022
1214
  })];
1023
1215
  }
1024
- function sb(kind, props, on, children, target) {
1025
- const hostProps = { kind };
1026
- if (props) hostProps.props = props;
1027
- if (on) hostProps.on = on;
1028
- if (target) hostProps.target = target;
1029
- return children === void 0 ? createElement("sb", hostProps) : createElement("sb", hostProps, children);
1030
- }
1031
1216
  const CoordinateSpacesContext = createContext({});
1032
1217
  function useCoordinateSpaces() {
1033
1218
  return useContext(CoordinateSpacesContext);
@@ -6948,6 +7133,7 @@ export {
6948
7133
  RadialGradient,
6949
7134
  Rectangle,
6950
7135
  RegisteredView,
7136
+ RemoteTree,
6951
7137
  ResponseStream,
6952
7138
  RoundedRectangle,
6953
7139
  Row,
@@ -6993,6 +7179,7 @@ export {
6993
7179
  color,
6994
7180
  configureSignInWithApple,
6995
7181
  createFileManager,
7182
+ createRemoteRoot,
6996
7183
  createSwiftUIRoot,
6997
7184
  exportFile,
6998
7185
  fileManager,