@swiftbrowser/react 0.21.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 +42 -1
- package/dist/components/remoteTree.d.ts +16 -0
- package/dist/components/remoteTree.d.ts.map +1 -0
- package/dist/host/instance.d.ts +11 -1
- package/dist/host/instance.d.ts.map +1 -1
- package/dist/host/remote.d.ts +48 -0
- package/dist/host/remote.d.ts.map +1 -0
- package/dist/host/root.d.ts +20 -2
- package/dist/host/root.d.ts.map +1 -1
- package/dist/host/tree.d.ts +15 -1
- package/dist/host/tree.d.ts.map +1 -1
- package/dist/index.d.ts +4 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +209 -22
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -239,7 +239,11 @@ Italic, Underline) comes back into the text. A `selection` binding, on a
|
|
|
239
239
|
plain or an attributed editor, gets the viewer's selection in UTF-16
|
|
240
240
|
offsets, so a Bold button can set `AttributedText.settingAttributes(text,
|
|
241
241
|
start, end, { bold: true })` on it; a selection the app sets moves the
|
|
242
|
-
caret (when the app focuses the editor, if it is not focused).
|
|
242
|
+
caret (when the app focuses the editor, if it is not focused). A text the
|
|
243
|
+
app sets that is not the echo of what the viewer typed (another person's
|
|
244
|
+
edit) keeps the caret in place: the app's `selection` is applied even when
|
|
245
|
+
its numbers did not change, and without one the caret is moved past the
|
|
246
|
+
change.
|
|
243
247
|
`.scrollDisabled` makes an editor as tall as its lines, for one in a
|
|
244
248
|
scrolling page:
|
|
245
249
|
|
|
@@ -979,6 +983,43 @@ side and provided again inside:
|
|
|
979
983
|
The list must be the same on every render of that component (each entry is
|
|
980
984
|
read with a hook). Props, callbacks and external stores need no bridging.
|
|
981
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
|
+
|
|
982
1023
|
## How it works
|
|
983
1024
|
|
|
984
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"}
|
package/dist/host/instance.d.ts
CHANGED
|
@@ -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
|
-
/**
|
|
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
|
|
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"}
|
package/dist/host/root.d.ts
CHANGED
|
@@ -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
|
-
|
|
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
|
package/dist/host/root.d.ts.map
CHANGED
|
@@ -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,
|
|
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"}
|
package/dist/host/tree.d.ts
CHANGED
|
@@ -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
|
-
|
|
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. */
|
package/dist/host/tree.d.ts.map
CHANGED
|
@@ -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;
|
|
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';
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,aAAa,EAAE,KAAK,2BAA2B,EAAE,KAAK,WAAW,EAAE,KAAK,oBAAoB,EAAE,MAAM,aAAa,CAAC;
|
|
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,
|
|
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
|
|
291
|
-
for (const grandchild of child.children) visit(grandchild, entry.children,
|
|
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
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
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
|
-
|
|
406
|
-
|
|
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,
|