@astrale-os/shell-react 0.4.0-beta.21 → 0.4.0-beta.22
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.
|
@@ -4,6 +4,7 @@ import type { ShellProviderProps } from '../session/provider.js';
|
|
|
4
4
|
export interface AstraleProps {
|
|
5
5
|
/** Defaults to the child-side sandbox handshake; pass a Shell for an embedded host or test. */
|
|
6
6
|
readonly config?: ShellConfig | Shell;
|
|
7
|
+
/** Shared loading UI kept mounted across boot and initial content suspension. */
|
|
7
8
|
readonly fallback?: ReactNode;
|
|
8
9
|
readonly errorFallback?: ShellProviderProps['errorFallback'];
|
|
9
10
|
readonly onError?: (error: unknown) => void;
|
|
@@ -1,12 +1,25 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { Suspense } from 'react';
|
|
1
|
+
import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Suspense, useLayoutEffect, useState } from 'react';
|
|
3
3
|
import { ShellProvider } from '../session/provider.js';
|
|
4
4
|
import { ViewStage } from '../window/stage.js';
|
|
5
5
|
import { defaultViewError, defaultViewLoading, ViewErrorBoundary } from './boundaries.js';
|
|
6
6
|
const SANDBOXED_SHELL = Object.freeze({ mode: 'sandboxed' });
|
|
7
7
|
/** Complete Astrale runtime root without taking ownership of application routing. */
|
|
8
8
|
export function Astrale(props) {
|
|
9
|
-
const loading =
|
|
9
|
+
const [loading, setLoading] = useState(true);
|
|
10
|
+
const loadingFallback = _jsx(LoadingPresence, { onChange: setLoading });
|
|
10
11
|
const errorFallback = props.errorFallback ?? defaultViewError;
|
|
11
|
-
return (_jsxs(ShellProvider, { config: props.config ?? SANDBOXED_SHELL, fallback:
|
|
12
|
+
return (_jsxs(_Fragment, { children: [loading ? (props.fallback ?? defaultViewLoading()) : null, _jsxs(ShellProvider, { config: props.config ?? SANDBOXED_SHELL, fallback: loadingFallback, errorFallback: errorFallback, onError: ({ error }) => props.onError?.(error), children: [_jsx(ViewErrorBoundary, { fallback: errorFallback, onError: props.onError, children: _jsx(Suspense, { fallback: loadingFallback, children: props.children }) }), _jsx(ViewStage, {})] })] }));
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Boot and content fallbacks are mutually exclusive. Their layout-effect handoffs are
|
|
16
|
+
* batched before paint, so boot → content suspension keeps the same animated DOM
|
|
17
|
+
* node. Cleanup also releases it on errors, retries, and Suspense reveals.
|
|
18
|
+
*/
|
|
19
|
+
function LoadingPresence({ onChange }) {
|
|
20
|
+
useLayoutEffect(() => {
|
|
21
|
+
onChange(true);
|
|
22
|
+
return () => onChange(false);
|
|
23
|
+
}, [onChange]);
|
|
24
|
+
return null;
|
|
12
25
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@astrale-os/shell-react",
|
|
3
|
-
"version": "0.4.0-beta.
|
|
3
|
+
"version": "0.4.0-beta.22",
|
|
4
4
|
"description": "Astrale shell-react — the React face of the shell (provider, hooks, view components)",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"repository": {
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
"zod": "4.4.3"
|
|
33
33
|
},
|
|
34
34
|
"devDependencies": {
|
|
35
|
-
"@astrale-os/sdk": "0.5.0-beta.
|
|
35
|
+
"@astrale-os/sdk": "0.5.0-beta.147",
|
|
36
36
|
"@testing-library/dom": "^10.4.0",
|
|
37
37
|
"@testing-library/react": "^16.3.0",
|
|
38
38
|
"@types/node": "^26.2.0",
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { Shell, ShellConfig } from '@astrale-os/shell'
|
|
2
2
|
import type { ReactElement, ReactNode } from 'react'
|
|
3
3
|
|
|
4
|
-
import { Suspense } from 'react'
|
|
4
|
+
import { Suspense, useLayoutEffect, useState } from 'react'
|
|
5
5
|
|
|
6
6
|
import type { ShellProviderProps } from '../session/provider.js'
|
|
7
7
|
|
|
@@ -14,6 +14,7 @@ const SANDBOXED_SHELL = Object.freeze({ mode: 'sandboxed' as const })
|
|
|
14
14
|
export interface AstraleProps {
|
|
15
15
|
/** Defaults to the child-side sandbox handshake; pass a Shell for an embedded host or test. */
|
|
16
16
|
readonly config?: ShellConfig | Shell
|
|
17
|
+
/** Shared loading UI kept mounted across boot and initial content suspension. */
|
|
17
18
|
readonly fallback?: ReactNode
|
|
18
19
|
readonly errorFallback?: ShellProviderProps['errorFallback']
|
|
19
20
|
readonly onError?: (error: unknown) => void
|
|
@@ -22,19 +23,36 @@ export interface AstraleProps {
|
|
|
22
23
|
|
|
23
24
|
/** Complete Astrale runtime root without taking ownership of application routing. */
|
|
24
25
|
export function Astrale(props: AstraleProps): ReactElement {
|
|
25
|
-
const loading =
|
|
26
|
+
const [loading, setLoading] = useState(true)
|
|
27
|
+
const loadingFallback = <LoadingPresence onChange={setLoading} />
|
|
26
28
|
const errorFallback = props.errorFallback ?? defaultViewError
|
|
27
29
|
return (
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
30
|
+
<>
|
|
31
|
+
{loading ? (props.fallback ?? defaultViewLoading()) : null}
|
|
32
|
+
<ShellProvider
|
|
33
|
+
config={props.config ?? SANDBOXED_SHELL}
|
|
34
|
+
fallback={loadingFallback}
|
|
35
|
+
errorFallback={errorFallback}
|
|
36
|
+
onError={({ error }) => props.onError?.(error)}
|
|
37
|
+
>
|
|
38
|
+
<ViewErrorBoundary fallback={errorFallback} onError={props.onError}>
|
|
39
|
+
<Suspense fallback={loadingFallback}>{props.children}</Suspense>
|
|
40
|
+
</ViewErrorBoundary>
|
|
41
|
+
<ViewStage />
|
|
42
|
+
</ShellProvider>
|
|
43
|
+
</>
|
|
39
44
|
)
|
|
40
45
|
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Boot and content fallbacks are mutually exclusive. Their layout-effect handoffs are
|
|
49
|
+
* batched before paint, so boot → content suspension keeps the same animated DOM
|
|
50
|
+
* node. Cleanup also releases it on errors, retries, and Suspense reveals.
|
|
51
|
+
*/
|
|
52
|
+
function LoadingPresence({ onChange }: { readonly onChange: (loading: boolean) => void }): null {
|
|
53
|
+
useLayoutEffect(() => {
|
|
54
|
+
onChange(true)
|
|
55
|
+
return () => onChange(false)
|
|
56
|
+
}, [onChange])
|
|
57
|
+
return null
|
|
58
|
+
}
|