@astrale-os/shell-react 0.2.7 → 0.2.9

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.
@@ -1,7 +1,8 @@
1
1
  import type { NodeRefInput } from '@astrale-os/kernel-client';
2
- import type { ApplicationNode, AppSession, IntentRegistry, SandboxProfile } from '@astrale-os/shell';
2
+ import type { ApplicationNode, AppSession, IntentRegistry } from '@astrale-os/shell';
3
3
  import type { CSSProperties, ReactNode } from 'react';
4
- import type { ViewHostHandle } from '../../window/host.js';
4
+ import type { SandboxPolicy, ViewHostHandle } from '../../window/host.js';
5
+ import type { ViewDescriptor } from '../../window/lifecycle/mount.js';
5
6
  import type { WindowState } from '../../window/lifecycle/states.js';
6
7
  /** The states `AppHost`'s fallback answers — the frame's, plus the two OS phases. */
7
8
  export type AppHostFallbackState = WindowState | {
@@ -11,6 +12,8 @@ export type AppHostFallbackState = WindowState | {
11
12
  };
12
13
  export interface AppHostProps {
13
14
  app: ApplicationNode | NodeRefInput;
15
+ /** Override the entry View while keeping the application's delegated session alive. */
16
+ view?: NodeRefInput | ViewDescriptor;
14
17
  /**
15
18
  * Default target node delivered to the app's view as `targetNodeId` (API §12.2).
16
19
  * Container-anchored apps need one — the dock passes the current space. A deeper
@@ -21,7 +24,7 @@ export interface AppHostProps {
21
24
  capabilities?: {
22
25
  intents: readonly (keyof IntentRegistry)[];
23
26
  };
24
- sandbox?: SandboxProfile | null;
27
+ sandbox?: SandboxPolicy;
25
28
  fallback?: (state: AppHostFallbackState) => ReactNode;
26
29
  onSession?: (session: AppSession) => void;
27
30
  className?: string;
@@ -1 +1 @@
1
- {"version":3,"file":"app-host.d.ts","sourceRoot":"","sources":["../../../src/application/apps/app-host.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAA;AAC7D,OAAO,KAAK,EAAE,eAAe,EAAE,UAAU,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAA;AACpG,OAAO,KAAK,EAAE,aAAa,EAAgB,SAAS,EAAE,MAAM,OAAO,CAAA;AAWnE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAA;AAE1D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kCAAkC,CAAA;AAMnE,qFAAqF;AACrF,MAAM,MAAM,oBAAoB,GAC5B,WAAW,GACX;IAAE,QAAQ,CAAC,KAAK,EAAE,SAAS,CAAA;CAAE,GAC7B;IAAE,QAAQ,CAAC,KAAK,EAAE,SAAS,CAAA;CAAE,CAAA;AAEjC,MAAM,WAAW,YAAY;IAC3B,GAAG,EAAE,eAAe,GAAG,YAAY,CAAA;IACnC;;;;;OAKG;IACH,IAAI,CAAC,EAAE,YAAY,CAAA;IACnB,YAAY,CAAC,EAAE;QAAE,OAAO,EAAE,SAAS,CAAC,MAAM,cAAc,CAAC,EAAE,CAAA;KAAE,CAAA;IAC7D,OAAO,CAAC,EAAE,cAAc,GAAG,IAAI,CAAA;IAC/B,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,oBAAoB,KAAK,SAAS,CAAA;IACrD,SAAS,CAAC,EAAE,CAAC,OAAO,EAAE,UAAU,KAAK,IAAI,CAAA;IACzC,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,KAAK,CAAC,EAAE,aAAa,CAAA;CACtB;AAED,eAAO,MAAM,OAAO,yGAoEnB,CAAA"}
1
+ {"version":3,"file":"app-host.d.ts","sourceRoot":"","sources":["../../../src/application/apps/app-host.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAA;AAC7D,OAAO,KAAK,EAAE,eAAe,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAA;AACpF,OAAO,KAAK,EAAE,aAAa,EAAgB,SAAS,EAAE,MAAM,OAAO,CAAA;AAWnE,OAAO,KAAK,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAA;AACzE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iCAAiC,CAAA;AACrE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kCAAkC,CAAA;AAMnE,qFAAqF;AACrF,MAAM,MAAM,oBAAoB,GAC5B,WAAW,GACX;IAAE,QAAQ,CAAC,KAAK,EAAE,SAAS,CAAA;CAAE,GAC7B;IAAE,QAAQ,CAAC,KAAK,EAAE,SAAS,CAAA;CAAE,CAAA;AAEjC,MAAM,WAAW,YAAY;IAC3B,GAAG,EAAE,eAAe,GAAG,YAAY,CAAA;IACnC,uFAAuF;IACvF,IAAI,CAAC,EAAE,YAAY,GAAG,cAAc,CAAA;IACpC;;;;;OAKG;IACH,IAAI,CAAC,EAAE,YAAY,CAAA;IACnB,YAAY,CAAC,EAAE;QAAE,OAAO,EAAE,SAAS,CAAC,MAAM,cAAc,CAAC,EAAE,CAAA;KAAE,CAAA;IAC7D,OAAO,CAAC,EAAE,aAAa,CAAA;IACvB,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,oBAAoB,KAAK,SAAS,CAAA;IACrD,SAAS,CAAC,EAAE,CAAC,OAAO,EAAE,UAAU,KAAK,IAAI,CAAA;IACzC,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,KAAK,CAAC,EAAE,aAAa,CAAA;CACtB;AAED,eAAO,MAAM,OAAO,yGAqEnB,CAAA"}
@@ -53,7 +53,8 @@ export const AppHost = forwardRef(function AppHost(props, ref) {
53
53
  return box(fallback?.({ phase: 'crashed', error }) ?? null);
54
54
  if (session === null)
55
55
  return box(fallback?.({ phase: 'opening' }) ?? null);
56
- if (session.view === null)
56
+ const view = props.view ?? session.view;
57
+ if (view === null)
57
58
  return box(fallback?.({ phase: 'no-view' }) ?? null);
58
- return (_jsx(ViewHost, { ref: ref, view: session.view, node: props.node, credential: session.credential, capabilities: props.capabilities ?? { intents: [] }, sandbox: props.sandbox, fallback: fallback ? (s) => fallback(s) : undefined, className: className, style: style }));
59
+ return (_jsx(ViewHost, { ref: ref, view: view, node: props.node, credential: session.credential, capabilities: props.capabilities ?? { intents: [] }, sandbox: props.sandbox, fallback: fallback ? (s) => fallback(s) : undefined, className: className, style: style }));
59
60
  });
@@ -0,0 +1,7 @@
1
+ import type { DomainSearchResult } from '@astrale-os/shell';
2
+ import type { ReadState } from '../../graph/memory/read/envelope.js';
3
+ export interface DomainsResult extends ReadState {
4
+ readonly domains: readonly DomainSearchResult[];
5
+ }
6
+ export declare function useDomains(): DomainsResult;
7
+ //# sourceMappingURL=domains.hook.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"domains.hook.d.ts","sourceRoot":"","sources":["../../../src/application/apps/domains.hook.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAA;AAI3D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,qCAAqC,CAAA;AAQpE,MAAM,WAAW,aAAc,SAAQ,SAAS;IAC9C,QAAQ,CAAC,OAAO,EAAE,SAAS,kBAAkB,EAAE,CAAA;CAChD;AAED,wBAAgB,UAAU,IAAI,aAAa,CAoB1C"}
@@ -0,0 +1,18 @@
1
+ import { useMemo } from 'react';
2
+ import { useAsyncResource } from '../../graph/async.js';
3
+ import { useShell } from '../../session/shell.hook.js';
4
+ import { isOsPlaneAvailable } from '../user/user.store.js';
5
+ const NO_DOMAINS = [];
6
+ export function useDomains() {
7
+ const shell = useShell();
8
+ const available = isOsPlaneAvailable(shell);
9
+ const result = useAsyncResource(available ? () => shell.apps.searchDomains() : null, NO_DOMAINS, [shell, available]);
10
+ return useMemo(() => ({
11
+ domains: result.data,
12
+ pending: result.pending,
13
+ error: result.error,
14
+ live: false,
15
+ seq: 0,
16
+ refetch: result.refetch,
17
+ }), [result]);
18
+ }
@@ -7,6 +7,8 @@ export { useSpace } from './spaces/space.hook.js';
7
7
  export type { SpaceResult } from './spaces/space.hook.js';
8
8
  export { useApps } from './apps/apps.hook.js';
9
9
  export type { AppsResult } from './apps/apps.hook.js';
10
+ export { useDomains } from './apps/domains.hook.js';
11
+ export type { DomainsResult } from './apps/domains.hook.js';
10
12
  export { useOpenApp } from './apps/open-app.hook.js';
11
13
  export { AppHost } from './apps/app-host.js';
12
14
  export type { AppHostFallbackState, AppHostProps } from './apps/app-host.js';
@@ -14,5 +16,5 @@ export { AppLogo } from './apps/app-logo.js';
14
16
  export type { AppLogoProps } from './apps/app-logo.js';
15
17
  export { useRoles } from './roles.hook.js';
16
18
  export type { RolesResult } from './roles.hook.js';
17
- export type { ApplicationNode, AppSession, RoleNode, SpaceNode, UserNode } from '@astrale-os/shell';
19
+ export type { ApplicationNode, AppSession, DomainSearchResult, RoleNode, SpaceNode, UserNode, } from '@astrale-os/shell';
18
20
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/application/index.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAC7C,OAAO,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAA;AACnD,YAAY,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAA;AAC3D,OAAO,EAAE,eAAe,EAAE,MAAM,gCAAgC,CAAA;AAChE,YAAY,EAAE,kBAAkB,EAAE,MAAM,gCAAgC,CAAA;AACxE,OAAO,EAAE,QAAQ,EAAE,MAAM,wBAAwB,CAAA;AACjD,YAAY,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAA;AACzD,OAAO,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAC7C,YAAY,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAA;AACrD,OAAO,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAA;AACpD,OAAO,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAA;AAC5C,YAAY,EAAE,oBAAoB,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAA;AAC5E,OAAO,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAA;AAC5C,YAAY,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAA;AACtD,OAAO,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAA;AAC1C,YAAY,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAA;AAGlD,YAAY,EAAE,eAAe,EAAE,UAAU,EAAE,QAAQ,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/application/index.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAC7C,OAAO,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAA;AACnD,YAAY,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAA;AAC3D,OAAO,EAAE,eAAe,EAAE,MAAM,gCAAgC,CAAA;AAChE,YAAY,EAAE,kBAAkB,EAAE,MAAM,gCAAgC,CAAA;AACxE,OAAO,EAAE,QAAQ,EAAE,MAAM,wBAAwB,CAAA;AACjD,YAAY,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAA;AACzD,OAAO,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAC7C,YAAY,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAA;AACrD,OAAO,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAA;AACnD,YAAY,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAA;AAC3D,OAAO,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAA;AACpD,OAAO,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAA;AAC5C,YAAY,EAAE,oBAAoB,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAA;AAC5E,OAAO,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAA;AAC5C,YAAY,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAA;AACtD,OAAO,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAA;AAC1C,YAAY,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAA;AAGlD,YAAY,EACV,eAAe,EACf,UAAU,EACV,kBAAkB,EAClB,QAAQ,EACR,SAAS,EACT,QAAQ,GACT,MAAM,mBAAmB,CAAA"}
@@ -1,11 +1,13 @@
1
1
  // application — API §12.2. The OS-plane hooks over the shell package's
2
2
  // `shell.user/spaces/apps` namespaces + the window module: useUser, useSpaces,
3
- // useCurrentSpace, useSpace, useApps, useRoles, useOpenApp, AppHost, AppLogo.
3
+ // useCurrentSpace, useSpace, useApps, useDomains, useRoles, useOpenApp,
4
+ // AppHost, AppLogo.
4
5
  export { useUser } from './user/user.hook.js';
5
6
  export { useSpaces } from './spaces/spaces.hook.js';
6
7
  export { useCurrentSpace } from './spaces/current-space.hook.js';
7
8
  export { useSpace } from './spaces/space.hook.js';
8
9
  export { useApps } from './apps/apps.hook.js';
10
+ export { useDomains } from './apps/domains.hook.js';
9
11
  export { useOpenApp } from './apps/open-app.hook.js';
10
12
  export { AppHost } from './apps/app-host.js';
11
13
  export { AppLogo } from './apps/app-logo.js';
@@ -1 +1 @@
1
- {"version":3,"file":"user.store.d.ts","sourceRoot":"","sources":["../../../src/application/user/user.store.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,KAAK,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAA;AAExD,MAAM,MAAM,SAAS,GACjB;IAAE,MAAM,EAAE,SAAS,CAAC;IAAC,OAAO,EAAE,OAAO,CAAC,IAAI,CAAC,CAAA;CAAE,GAC7C;IAAE,MAAM,EAAE,UAAU,CAAC;IAAC,KAAK,EAAE,QAAQ,CAAA;CAAE,GACvC;IAAE,MAAM,EAAE,UAAU,CAAC;IAAC,KAAK,EAAE,OAAO,CAAA;CAAE;AACxC,qFAAqF;GACnF;IAAE,MAAM,EAAE,aAAa,CAAC;IAAC,KAAK,EAAE,OAAO,CAAA;CAAE,CAAA;AAE7C,eAAO,MAAM,SAAS,2BAAkC,CAAA;AAKxD,sEAAsE;AACtE,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,KAAK,GAAG,OAAO,CAExD;AAcD;;;;GAIG;AACH,wBAAsB,SAAS,CAAC,KAAK,EAAE,KAAK,GAAG,OAAO,CAAC,IAAI,CAAC,CAkB3D;AAED,wFAAwF;AACxF,wBAAgB,QAAQ,CAAC,KAAK,EAAE,KAAK,GAAG,SAAS,CAoBhD"}
1
+ {"version":3,"file":"user.store.d.ts","sourceRoot":"","sources":["../../../src/application/user/user.store.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,KAAK,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAA;AAExD,MAAM,MAAM,SAAS,GACjB;IAAE,MAAM,EAAE,SAAS,CAAC;IAAC,OAAO,EAAE,OAAO,CAAC,IAAI,CAAC,CAAA;CAAE,GAC7C;IAAE,MAAM,EAAE,UAAU,CAAC;IAAC,KAAK,EAAE,QAAQ,CAAA;CAAE,GACvC;IAAE,MAAM,EAAE,UAAU,CAAC;IAAC,KAAK,EAAE,OAAO,CAAA;CAAE;AACxC,qFAAqF;GACnF;IAAE,MAAM,EAAE,aAAa,CAAC;IAAC,KAAK,EAAE,OAAO,CAAA;CAAE,CAAA;AAE7C,eAAO,MAAM,SAAS,2BAAkC,CAAA;AAKxD,sEAAsE;AACtE,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,KAAK,GAAG,OAAO,CAExD;AAeD;;;;GAIG;AACH,wBAAsB,SAAS,CAAC,KAAK,EAAE,KAAK,GAAG,OAAO,CAAC,IAAI,CAAC,CAkB3D;AAED,wFAAwF;AACxF,wBAAgB,QAAQ,CAAC,KAAK,EAAE,KAAK,GAAG,SAAS,CAoBhD"}
@@ -7,7 +7,8 @@ export function isOsPlaneAvailable(shell) {
7
7
  }
8
8
  function osPlaneError(cause) {
9
9
  const detail = cause instanceof Error ? cause.message : String(cause);
10
- return new Error(`The OS plane is unavailable (useUser/useSpaces/useApps): shell.user.me() could not resolve ` +
10
+ return new Error(`The OS plane is unavailable (useUser/useSpaces/useApps/useDomains): ` +
11
+ `shell.user.me() could not resolve ` +
11
12
  `— the workspace/shell domain may not be installed for this identity. (${detail})`);
12
13
  }
13
14
  /**
package/dist/index.d.ts CHANGED
@@ -25,12 +25,12 @@ export type { LinkProps, MissingPolicy, NavAdapter, NavLocation, NavScopeHandle,
25
25
  export { SelectionScope, useSelection } from './selection/index.js';
26
26
  export type { Selection, SelectionScopeProps } from './selection/index.js';
27
27
  export { useCloseGuard, useTargetNode, useViewsFor, useWindow, useWindowList, useWindows, ViewHost, ViewStage, } from './window/index.js';
28
- export type { CloseGuard, CloseVerdict, ViewDescriptor, ViewHostProps, ViewSource, ViewHostHandle, ViewsForResult, WindowListEntry, WindowState, WindowView, } from './window/index.js';
28
+ export type { CloseGuard, CloseVerdict, ResolvedSource, SandboxPolicy, ViewDescriptor, ViewHostProps, ViewSource, ViewHostHandle, ViewsForResult, WindowListEntry, WindowState, WindowView, } from './window/index.js';
29
29
  export { useEmitIntent, useIntent, useSendIntent } from './intents/index.js';
30
30
  export type { EmitIntent, IntentCtx, IntentPayload, IntentReply, SendIntent, } from './intents/index.js';
31
- export { AppHost, AppLogo, useApps, useCurrentSpace, useOpenApp, useRoles, useSpace, useSpaces, useUser, } from './application/index.js';
32
- export type { AppHostFallbackState, AppHostProps, AppLogoProps, AppsResult, CurrentSpaceResult, RolesResult, SpaceResult, SpacesResult, } from './application/index.js';
33
- export type { ApplicationNode, AppSession, RoleNode, SpaceNode, UserNode } from '@astrale-os/shell';
31
+ export { AppHost, AppLogo, useApps, useCurrentSpace, useOpenApp, useRoles, useDomains, useSpace, useSpaces, useUser, } from './application/index.js';
32
+ export type { AppHostFallbackState, AppHostProps, AppLogoProps, AppsResult, CurrentSpaceResult, RolesResult, DomainsResult, SpaceResult, SpacesResult, } from './application/index.js';
33
+ export type { ApplicationNode, AppSession, DomainSearchResult, RoleNode, SpaceNode, UserNode, } from '@astrale-os/shell';
34
34
  export type { Action, ReadState, ReadOptions, Windowed } from './graph/index.js';
35
35
  export * from './graph/guards.error.js';
36
36
  export { DESCRIPTION_KEY, NAME_KEY, STATUS_KEY, descriptionOf, nameOf, normalizeRef, statusOf, } from '@astrale-os/kernel-client';
@@ -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
@@ -27,7 +27,7 @@ export { useCloseGuard, useTargetNode, useViewsFor, useWindow, useWindowList, us
27
27
  // ─── intents (§11) ───────────────────────────────────────────────────────────
28
28
  export { useEmitIntent, useIntent, useSendIntent } from './intents/index.js';
29
29
  // ─── application — the OS plane (§12) ────────────────────────────────────────
30
- export { AppHost, AppLogo, useApps, useCurrentSpace, useOpenApp, useRoles, useSpace, useSpaces, useUser, } from './application/index.js';
30
+ export { AppHost, AppLogo, useApps, useCurrentSpace, useOpenApp, useRoles, useDomains, useSpace, useSpaces, useUser, } from './application/index.js';
31
31
  // ─── error taxonomy (§1.4) — guards + classes, re-exported from kernel-client ─
32
32
  export * from './graph/guards.error.js';
33
33
  // ─── re-exported kernel-client types (one import surface, §0 preamble) ───────
@@ -1,7 +1,7 @@
1
1
  import type { NodeRefInput } from '@astrale-os/kernel-client';
2
2
  import type { CloseResult, IntentRegistry, MountCredential, SandboxProfile } from '@astrale-os/shell';
3
3
  import type { CSSProperties, ReactNode } from 'react';
4
- import type { ViewSource } from './lifecycle/mount.js';
4
+ import type { ResolvedSource, ViewSource } from './lifecycle/mount.js';
5
5
  import type { WindowState } from './lifecycle/states.js';
6
6
  /** Imperative handle on a mounted `ViewHost` (API §10). */
7
7
  export interface ViewHostHandle {
@@ -13,13 +13,15 @@ export interface ViewHostHandle {
13
13
  /** The one explicit remount. */
14
14
  refresh(): void;
15
15
  }
16
+ /** Static iframe sandbox flags, an explicit opt-out, or a policy over the resolved mount. */
17
+ export type SandboxPolicy = SandboxProfile | null | ((source: ResolvedSource) => SandboxProfile | null);
16
18
  export type ViewHostProps = ViewSource & {
17
19
  /** Node this view opens on — delivered as `targetNodeId`; a change retargets. */
18
20
  node?: NodeRefInput;
19
21
  capabilities?: {
20
22
  intents: readonly (keyof IntentRegistry)[];
21
23
  };
22
- sandbox?: SandboxProfile | null;
24
+ sandbox?: SandboxPolicy;
23
25
  credential?: MountCredential;
24
26
  /** Rendered inside the anchor while the frame is not `ready`. */
25
27
  fallback?: (state: WindowState) => ReactNode;
@@ -1 +1 @@
1
- {"version":3,"file":"host.d.ts","sourceRoot":"","sources":["../../src/window/host.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAA;AAC7D,OAAO,KAAK,EACV,WAAW,EACX,cAAc,EACd,eAAe,EAEf,cAAc,EAEf,MAAM,mBAAmB,CAAA;AAC1B,OAAO,KAAK,EAAE,aAAa,EAAgB,SAAS,EAAE,MAAM,OAAO,CAAA;AAqBnE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAA;AACtD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAA;AAcxD,2DAA2D;AAC3D,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAA;IAChC,KAAK,CAAC,IAAI,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,OAAO,CAAC;QAAC,SAAS,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,WAAW,CAAC,CAAA;IAC3E,gCAAgC;IAChC,OAAO,IAAI,IAAI,CAAA;CAChB;AAED,MAAM,MAAM,aAAa,GAAG,UAAU,GAAG;IACvC,iFAAiF;IACjF,IAAI,CAAC,EAAE,YAAY,CAAA;IACnB,YAAY,CAAC,EAAE;QAAE,OAAO,EAAE,SAAS,CAAC,MAAM,cAAc,CAAC,EAAE,CAAA;KAAE,CAAA;IAC7D,OAAO,CAAC,EAAE,cAAc,GAAG,IAAI,CAAA;IAC/B,UAAU,CAAC,EAAE,eAAe,CAAA;IAC5B,iEAAiE;IACjE,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,WAAW,KAAK,SAAS,CAAA;IAC5C,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,WAAW,KAAK,IAAI,CAAA;IAC5C,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,KAAK,CAAC,EAAE,aAAa,CAAA;CACtB,CAAA;AAYD,eAAO,MAAM,QAAQ,0GAyQpB,CAAA"}
1
+ {"version":3,"file":"host.d.ts","sourceRoot":"","sources":["../../src/window/host.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAA;AAC7D,OAAO,KAAK,EACV,WAAW,EACX,cAAc,EACd,eAAe,EAEf,cAAc,EAEf,MAAM,mBAAmB,CAAA;AAC1B,OAAO,KAAK,EAAE,aAAa,EAAgB,SAAS,EAAE,MAAM,OAAO,CAAA;AAqBnE,OAAO,KAAK,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAA;AACtE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAA;AAcxD,2DAA2D;AAC3D,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAA;IAChC,KAAK,CAAC,IAAI,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,OAAO,CAAC;QAAC,SAAS,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,WAAW,CAAC,CAAA;IAC3E,gCAAgC;IAChC,OAAO,IAAI,IAAI,CAAA;CAChB;AAED,6FAA6F;AAC7F,MAAM,MAAM,aAAa,GACrB,cAAc,GACd,IAAI,GACJ,CAAC,CAAC,MAAM,EAAE,cAAc,KAAK,cAAc,GAAG,IAAI,CAAC,CAAA;AAEvD,MAAM,MAAM,aAAa,GAAG,UAAU,GAAG;IACvC,iFAAiF;IACjF,IAAI,CAAC,EAAE,YAAY,CAAA;IACnB,YAAY,CAAC,EAAE;QAAE,OAAO,EAAE,SAAS,CAAC,MAAM,cAAc,CAAC,EAAE,CAAA;KAAE,CAAA;IAC7D,OAAO,CAAC,EAAE,aAAa,CAAA;IACvB,UAAU,CAAC,EAAE,eAAe,CAAA;IAC5B,iEAAiE;IACjE,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,WAAW,KAAK,SAAS,CAAA;IAC5C,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,WAAW,KAAK,IAAI,CAAA;IAC5C,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,KAAK,CAAC,EAAE,aAAa,CAAA;CACtB,CAAA;AAYD,eAAO,MAAM,QAAQ,0GA4RpB,CAAA"}
@@ -131,6 +131,14 @@ export const ViewHost = forwardRef(function ViewHost(props, ref) {
131
131
  else
132
132
  hub.stashForAdopt(mountKey, record);
133
133
  }
134
+ /** A pre-ready failure must not leave an empty stage slot over the fallback UI. */
135
+ function crashAndRelease(error) {
136
+ const slot = slotRef.current;
137
+ slotRef.current = null;
138
+ lastRectRef.current = null;
139
+ slot?.release();
140
+ setStateSafe({ phase: 'crashed', windowId: windowIdRef.current ?? undefined, error });
141
+ }
134
142
  // Adopt a live window a just-unmounted same-source host left behind.
135
143
  if (!wasRefresh) {
136
144
  const adopted = hub.takeAdopt(mountKey);
@@ -160,20 +168,32 @@ export const ViewHost = forwardRef(function ViewHost(props, ref) {
160
168
  catch (err) {
161
169
  // No window yet (source resolution failed) — windowId honestly absent.
162
170
  if (!cancelled)
163
- setStateSafe({ phase: 'crashed', error: err });
171
+ crashAndRelease(err);
164
172
  return;
165
173
  }
166
174
  if (cancelled)
167
175
  return;
168
176
  const usedHandshake = handshake === 'none' ? 'none' : resolved.handshake;
169
177
  const deliveredTarget = refToId(p.node);
178
+ let sandbox;
179
+ try {
180
+ sandbox =
181
+ typeof p.sandbox === 'function'
182
+ ? p.sandbox({ ...resolved, handshake: usedHandshake })
183
+ : p.sandbox;
184
+ }
185
+ catch (err) {
186
+ if (!cancelled)
187
+ crashAndRelease(err);
188
+ return;
189
+ }
170
190
  const params = {
171
191
  host: slotRef.current.element,
172
192
  url: resolved.url,
173
193
  functionId: resolved.functionId,
174
194
  capabilities: { intents: p.capabilities?.intents ?? [] },
175
195
  targetNodeId: deliveredTarget,
176
- sandbox: p.sandbox,
196
+ sandbox,
177
197
  handshake: usedHandshake,
178
198
  credential: p.credential,
179
199
  };
@@ -206,7 +226,7 @@ export const ViewHost = forwardRef(function ViewHost(props, ref) {
206
226
  await mountWith('none', true);
207
227
  return;
208
228
  }
209
- setStateSafe({ phase: 'crashed', windowId: windowIdRef.current ?? undefined, error: err });
229
+ crashAndRelease(err);
210
230
  }
211
231
  }
212
232
  function begin(controller) {
@@ -1,6 +1,6 @@
1
1
  export { ViewStage } from './stage.js';
2
2
  export { ViewHost } from './host.js';
3
- export type { ViewHostProps, ViewHostHandle } from './host.js';
3
+ export type { SandboxPolicy, ViewHostProps, ViewHostHandle } from './host.js';
4
4
  export { useWindows, useWindowList } from './hooks/windows.hook.js';
5
5
  export type { WindowListEntry } from './hooks/windows.hook.js';
6
6
  export { useWindow } from './hooks/window.hook.js';
@@ -10,6 +10,6 @@ export type { ViewsForResult } from './hooks/views-for.hook.js';
10
10
  export { useTargetNode } from './hooks/target-node.hook.js';
11
11
  export { useCloseGuard } from './hooks/close-guard.hook.js';
12
12
  export type { CloseGuard, CloseVerdict } from './hooks/close-guard.hook.js';
13
- export type { ViewDescriptor, ViewSource, Node } from './lifecycle/mount.js';
13
+ export type { ResolvedSource, ViewDescriptor, ViewSource, Node } from './lifecycle/mount.js';
14
14
  export type { WindowState } from './lifecycle/states.js';
15
15
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/window/index.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAA;AACtC,OAAO,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAA;AACpC,YAAY,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,WAAW,CAAA;AAC9D,OAAO,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAA;AACnE,YAAY,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAA;AAC9D,OAAO,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAA;AAClD,YAAY,EAAE,UAAU,EAAE,MAAM,UAAU,CAAA;AAC1C,OAAO,EAAE,WAAW,EAAE,MAAM,2BAA2B,CAAA;AACvD,YAAY,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAA;AAC/D,OAAO,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAA;AAC3D,OAAO,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAA;AAC3D,YAAY,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAA;AAC3E,YAAY,EAAE,cAAc,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,sBAAsB,CAAA;AAC5E,YAAY,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/window/index.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAA;AACtC,OAAO,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAA;AACpC,YAAY,EAAE,aAAa,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,WAAW,CAAA;AAC7E,OAAO,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAA;AACnE,YAAY,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAA;AAC9D,OAAO,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAA;AAClD,YAAY,EAAE,UAAU,EAAE,MAAM,UAAU,CAAA;AAC1C,OAAO,EAAE,WAAW,EAAE,MAAM,2BAA2B,CAAA;AACvD,YAAY,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAA;AAC/D,OAAO,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAA;AAC3D,OAAO,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAA;AAC3D,YAAY,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAA;AAC3E,YAAY,EAAE,cAAc,EAAE,cAAc,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,sBAAsB,CAAA;AAC5F,YAAY,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"mount.d.ts","sourceRoot":"","sources":["../../../src/window/lifecycle/mount.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAA;AAC7D;;;;;;GAMG;AACH,OAAO,KAAK,EAAE,YAAY,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAA;AAE5D,+EAA+E;AAC/E,MAAM,MAAM,IAAI,GAAG,WAAW,CAAC,OAAO,CAAC,UAAU,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAA;AAExE,kFAAkF;AAClF,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;IACnB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAC5B;AAED;;;;GAIG;AACH,MAAM,MAAM,UAAU,GAAG;IAAE,QAAQ,CAAC,IAAI,EAAE,YAAY,GAAG,cAAc,CAAA;CAAE,GAAG;IAAE,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;CAAE,CAAA;AAEpG,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;IACpB,QAAQ,CAAC,SAAS,EAAE,OAAO,GAAG,MAAM,CAAA;IACpC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAA;CAC5B;AA6CD,4EAA4E;AAC5E,wBAAsB,aAAa,CACjC,MAAM,EAAE,YAAY,EACpB,MAAM,EAAE,UAAU,GACjB,OAAO,CAAC,cAAc,CAAC,CAgBzB;AAED,uEAAuE;AACvE,wBAAgB,SAAS,CAAC,MAAM,EAAE,UAAU,GAAG,MAAM,CAOpD;AAED,wEAAwE;AACxE,wBAAgB,OAAO,CAAC,GAAG,EAAE,YAAY,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,CAKzE;AAED;;;GAGG;AACH,wBAAgB,4BAA4B,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAGlE;AAED,mFAAmF;AACnF,wBAAgB,aAAa,CAAC,KAAK,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,CAMlF"}
1
+ {"version":3,"file":"mount.d.ts","sourceRoot":"","sources":["../../../src/window/lifecycle/mount.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAA;AAC7D;;;;;;GAMG;AACH,OAAO,KAAK,EAAE,YAAY,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAA;AAE5D,+EAA+E;AAC/E,MAAM,MAAM,IAAI,GAAG,WAAW,CAAC,OAAO,CAAC,UAAU,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAA;AAExE,kFAAkF;AAClF,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;IACnB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAC5B;AAED;;;;GAIG;AACH,MAAM,MAAM,UAAU,GAAG;IAAE,QAAQ,CAAC,IAAI,EAAE,YAAY,GAAG,cAAc,CAAA;CAAE,GAAG;IAAE,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;CAAE,CAAA;AAEpG,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;IACpB,QAAQ,CAAC,SAAS,EAAE,OAAO,GAAG,MAAM,CAAA;IACpC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAA;CAC5B;AA2ED,4EAA4E;AAC5E,wBAAsB,aAAa,CACjC,MAAM,EAAE,YAAY,EACpB,MAAM,EAAE,UAAU,GACjB,OAAO,CAAC,cAAc,CAAC,CAgBzB;AAED,uEAAuE;AACvE,wBAAgB,SAAS,CAAC,MAAM,EAAE,UAAU,GAAG,MAAM,CAOpD;AAED,wEAAwE;AACxE,wBAAgB,OAAO,CAAC,GAAG,EAAE,YAAY,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,CAKzE;AAED;;;GAGG;AACH,wBAAgB,4BAA4B,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAGlE;AAED,mFAAmF;AACnF,wBAAgB,aAAa,CAAC,KAAK,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,CAMlF"}
@@ -24,7 +24,7 @@ async function resolveViewRef(kernel, ref, handshakeHint) {
24
24
  if (node === null)
25
25
  throw new Error(`ViewHost: view not found (${toPath(ref)})`);
26
26
  const props = node.props ?? {};
27
- const url = props.url;
27
+ const url = viewUrl(props);
28
28
  if (typeof url !== 'string' || url.length === 0) {
29
29
  throw new Error('ViewHost: resolved view has no url');
30
30
  }
@@ -32,6 +32,37 @@ async function resolveViewRef(kernel, ref, handshakeHint) {
32
32
  const id = node.id ?? slugOfUrl(url);
33
33
  return { url, handshake, functionId: id };
34
34
  }
35
+ /** Resolve both the legacy flat url and the canonical Function binding. */
36
+ function viewUrl(props) {
37
+ if (typeof props.url === 'string' && props.url.length > 0)
38
+ return props.url;
39
+ const direct = remoteUrlOf(props.binding);
40
+ if (direct !== undefined)
41
+ return direct;
42
+ // Raw graph props can be keyed by the property's canonical graph key rather
43
+ // than its local slug. The binding value remains self-describing.
44
+ for (const value of Object.values(props)) {
45
+ const remoteUrl = remoteUrlOf(value);
46
+ if (remoteUrl !== undefined)
47
+ return remoteUrl;
48
+ }
49
+ return undefined;
50
+ }
51
+ function remoteUrlOf(value) {
52
+ let binding = value;
53
+ if (typeof binding === 'string') {
54
+ try {
55
+ binding = JSON.parse(binding);
56
+ }
57
+ catch {
58
+ return undefined;
59
+ }
60
+ }
61
+ if (binding === null || typeof binding !== 'object')
62
+ return undefined;
63
+ const remoteUrl = binding.remoteUrl;
64
+ return typeof remoteUrl === 'string' && remoteUrl.length > 0 ? remoteUrl : undefined;
65
+ }
35
66
  function normalizeHandshake(value) {
36
67
  return value === 'none' ? 'none' : 'shell';
37
68
  }
@@ -1 +1 @@
1
- {"version":3,"file":"stage.d.ts","sourceRoot":"","sources":["../../src/window/stage.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAiB,YAAY,EAAE,MAAM,OAAO,CAAA;AA4BxD,wBAAgB,SAAS,IAAI,YAAY,CAiBxC"}
1
+ {"version":3,"file":"stage.d.ts","sourceRoot":"","sources":["../../src/window/stage.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAiB,YAAY,EAAE,MAAM,OAAO,CAAA;AA6BxD,wBAAgB,SAAS,IAAI,YAAY,CAiBxC"}
@@ -20,6 +20,7 @@ const STAGE_STYLE = {
20
20
  left: 0,
21
21
  right: 0,
22
22
  bottom: 0,
23
+ zIndex: 20,
23
24
  pointerEvents: 'none',
24
25
  };
25
26
  export function ViewStage() {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@astrale-os/shell-react",
3
- "version": "0.2.7",
3
+ "version": "0.2.9",
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": {
@@ -44,7 +44,7 @@
44
44
  "react-dom": "^19.2.0",
45
45
  "typescript": "~6.0.1-rc",
46
46
  "vitest": "^3.2.4",
47
- "@astrale-os/shell": "0.3.5"
47
+ "@astrale-os/shell": "0.3.7"
48
48
  },
49
49
  "peerDependencies": {
50
50
  "@astrale-os/shell": ">=0.2.0 <1.0.0",
@@ -1,5 +1,5 @@
1
1
  import type { NodeRefInput } from '@astrale-os/kernel-client'
2
- import type { ApplicationNode, AppSession, IntentRegistry, SandboxProfile } from '@astrale-os/shell'
2
+ import type { ApplicationNode, AppSession, IntentRegistry } from '@astrale-os/shell'
3
3
  import type { CSSProperties, ReactElement, ReactNode } from 'react'
4
4
 
5
5
  /**
@@ -11,7 +11,7 @@ import type { CSSProperties, ReactElement, ReactNode } from 'react'
11
11
  */
12
12
  import { forwardRef, useEffect, useRef, useState } from 'react'
13
13
 
14
- import type { ViewHostHandle } from '../../window/host.js'
14
+ import type { SandboxPolicy, ViewHostHandle } from '../../window/host.js'
15
15
  import type { ViewDescriptor } from '../../window/lifecycle/mount.js'
16
16
  import type { WindowState } from '../../window/lifecycle/states.js'
17
17
 
@@ -27,6 +27,8 @@ export type AppHostFallbackState =
27
27
 
28
28
  export interface AppHostProps {
29
29
  app: ApplicationNode | NodeRefInput
30
+ /** Override the entry View while keeping the application's delegated session alive. */
31
+ view?: NodeRefInput | ViewDescriptor
30
32
  /**
31
33
  * Default target node delivered to the app's view as `targetNodeId` (API §12.2).
32
34
  * Container-anchored apps need one — the dock passes the current space. A deeper
@@ -35,7 +37,7 @@ export interface AppHostProps {
35
37
  */
36
38
  node?: NodeRefInput
37
39
  capabilities?: { intents: readonly (keyof IntentRegistry)[] }
38
- sandbox?: SandboxProfile | null
40
+ sandbox?: SandboxPolicy
39
41
  fallback?: (state: AppHostFallbackState) => ReactNode
40
42
  onSession?: (session: AppSession) => void
41
43
  className?: string
@@ -94,12 +96,13 @@ export const AppHost = forwardRef<ViewHostHandle, AppHostProps>(
94
96
  // A session-open failure has no window yet — `windowId` is honestly absent (§10).
95
97
  if (error !== undefined) return box(fallback?.({ phase: 'crashed', error }) ?? null)
96
98
  if (session === null) return box(fallback?.({ phase: 'opening' }) ?? null)
97
- if (session.view === null) return box(fallback?.({ phase: 'no-view' }) ?? null)
99
+ const view = props.view ?? session.view
100
+ if (view === null) return box(fallback?.({ phase: 'no-view' }) ?? null)
98
101
 
99
102
  return (
100
103
  <ViewHost
101
104
  ref={ref}
102
- view={session.view as ViewDescriptor}
105
+ view={view}
103
106
  node={props.node}
104
107
  credential={session.credential}
105
108
  capabilities={props.capabilities ?? { intents: [] }}
@@ -0,0 +1,37 @@
1
+ import type { DomainSearchResult } from '@astrale-os/shell'
2
+
3
+ import { useMemo } from 'react'
4
+
5
+ import type { ReadState } from '../../graph/memory/read/envelope.js'
6
+
7
+ import { useAsyncResource } from '../../graph/async.js'
8
+ import { useShell } from '../../session/shell.hook.js'
9
+ import { isOsPlaneAvailable } from '../user/user.store.js'
10
+
11
+ const NO_DOMAINS: readonly DomainSearchResult[] = []
12
+
13
+ export interface DomainsResult extends ReadState {
14
+ readonly domains: readonly DomainSearchResult[]
15
+ }
16
+
17
+ export function useDomains(): DomainsResult {
18
+ const shell = useShell()
19
+ const available = isOsPlaneAvailable(shell)
20
+ const result = useAsyncResource<readonly DomainSearchResult[]>(
21
+ available ? () => shell.apps.searchDomains() : null,
22
+ NO_DOMAINS,
23
+ [shell, available],
24
+ )
25
+
26
+ return useMemo(
27
+ () => ({
28
+ domains: result.data,
29
+ pending: result.pending,
30
+ error: result.error,
31
+ live: false,
32
+ seq: 0,
33
+ refetch: result.refetch,
34
+ }),
35
+ [result],
36
+ )
37
+ }
@@ -1,6 +1,7 @@
1
1
  // application — API §12.2. The OS-plane hooks over the shell package's
2
2
  // `shell.user/spaces/apps` namespaces + the window module: useUser, useSpaces,
3
- // useCurrentSpace, useSpace, useApps, useRoles, useOpenApp, AppHost, AppLogo.
3
+ // useCurrentSpace, useSpace, useApps, useDomains, useRoles, useOpenApp,
4
+ // AppHost, AppLogo.
4
5
  export { useUser } from './user/user.hook.js'
5
6
  export { useSpaces } from './spaces/spaces.hook.js'
6
7
  export type { SpacesResult } from './spaces/spaces.hook.js'
@@ -10,6 +11,8 @@ export { useSpace } from './spaces/space.hook.js'
10
11
  export type { SpaceResult } from './spaces/space.hook.js'
11
12
  export { useApps } from './apps/apps.hook.js'
12
13
  export type { AppsResult } from './apps/apps.hook.js'
14
+ export { useDomains } from './apps/domains.hook.js'
15
+ export type { DomainsResult } from './apps/domains.hook.js'
13
16
  export { useOpenApp } from './apps/open-app.hook.js'
14
17
  export { AppHost } from './apps/app-host.js'
15
18
  export type { AppHostFallbackState, AppHostProps } from './apps/app-host.js'
@@ -19,4 +22,11 @@ export { useRoles } from './roles.hook.js'
19
22
  export type { RolesResult } from './roles.hook.js'
20
23
 
21
24
  // The OS node/session types (from the shell package, single import surface).
22
- export type { ApplicationNode, AppSession, RoleNode, SpaceNode, UserNode } from '@astrale-os/shell'
25
+ export type {
26
+ ApplicationNode,
27
+ AppSession,
28
+ DomainSearchResult,
29
+ RoleNode,
30
+ SpaceNode,
31
+ UserNode,
32
+ } from '@astrale-os/shell'
@@ -31,7 +31,8 @@ interface UserSurface {
31
31
  function osPlaneError(cause: unknown): Error {
32
32
  const detail = cause instanceof Error ? cause.message : String(cause)
33
33
  return new Error(
34
- `The OS plane is unavailable (useUser/useSpaces/useApps): shell.user.me() could not resolve ` +
34
+ `The OS plane is unavailable (useUser/useSpaces/useApps/useDomains): ` +
35
+ `shell.user.me() could not resolve ` +
35
36
  `— the workspace/shell domain may not be installed for this identity. (${detail})`,
36
37
  )
37
38
  }
package/src/index.ts CHANGED
@@ -106,6 +106,8 @@ export {
106
106
  export type {
107
107
  CloseGuard,
108
108
  CloseVerdict,
109
+ ResolvedSource,
110
+ SandboxPolicy,
109
111
  ViewDescriptor,
110
112
  ViewHostProps,
111
113
  ViewSource,
@@ -134,6 +136,7 @@ export {
134
136
  useCurrentSpace,
135
137
  useOpenApp,
136
138
  useRoles,
139
+ useDomains,
137
140
  useSpace,
138
141
  useSpaces,
139
142
  useUser,
@@ -145,11 +148,19 @@ export type {
145
148
  AppsResult,
146
149
  CurrentSpaceResult,
147
150
  RolesResult,
151
+ DomainsResult,
148
152
  SpaceResult,
149
153
  SpacesResult,
150
154
  } from './application/index.js'
151
155
  // OS node/session types (from the shell package, single import surface).
152
- export type { ApplicationNode, AppSession, RoleNode, SpaceNode, UserNode } from '@astrale-os/shell'
156
+ export type {
157
+ ApplicationNode,
158
+ AppSession,
159
+ DomainSearchResult,
160
+ RoleNode,
161
+ SpaceNode,
162
+ UserNode,
163
+ } from '@astrale-os/shell'
153
164
 
154
165
  // ─── shared envelopes (§1) ───────────────────────────────────────────────────
155
166
  export type { Action, ReadState, ReadOptions, Windowed } from './graph/index.js'
@@ -28,7 +28,7 @@ import {
28
28
  } from 'react'
29
29
 
30
30
  import type { AdoptRecord } from './adopt.js'
31
- import type { ViewSource } from './lifecycle/mount.js'
31
+ import type { ResolvedSource, ViewSource } from './lifecycle/mount.js'
32
32
  import type { WindowState } from './lifecycle/states.js'
33
33
  import type { SlotRect, StageController, StageSlot } from './placement.js'
34
34
 
@@ -51,11 +51,17 @@ export interface ViewHostHandle {
51
51
  refresh(): void
52
52
  }
53
53
 
54
+ /** Static iframe sandbox flags, an explicit opt-out, or a policy over the resolved mount. */
55
+ export type SandboxPolicy =
56
+ | SandboxProfile
57
+ | null
58
+ | ((source: ResolvedSource) => SandboxProfile | null)
59
+
54
60
  export type ViewHostProps = ViewSource & {
55
61
  /** Node this view opens on — delivered as `targetNodeId`; a change retargets. */
56
62
  node?: NodeRefInput
57
63
  capabilities?: { intents: readonly (keyof IntentRegistry)[] }
58
- sandbox?: SandboxProfile | null
64
+ sandbox?: SandboxPolicy
59
65
  credential?: MountCredential
60
66
  /** Rendered inside the anchor while the frame is not `ready`. */
61
67
  fallback?: (state: WindowState) => ReactNode
@@ -204,6 +210,15 @@ export const ViewHost = forwardRef<ViewHostHandle, ViewHostProps>(
204
210
  else hub.stashForAdopt(mountKey, record)
205
211
  }
206
212
 
213
+ /** A pre-ready failure must not leave an empty stage slot over the fallback UI. */
214
+ function crashAndRelease(error: unknown): void {
215
+ const slot = slotRef.current
216
+ slotRef.current = null
217
+ lastRectRef.current = null
218
+ slot?.release()
219
+ setStateSafe({ phase: 'crashed', windowId: windowIdRef.current ?? undefined, error })
220
+ }
221
+
207
222
  // Adopt a live window a just-unmounted same-source host left behind.
208
223
  if (!wasRefresh) {
209
224
  const adopted = hub.takeAdopt(mountKey)
@@ -232,20 +247,30 @@ export const ViewHost = forwardRef<ViewHostHandle, ViewHostProps>(
232
247
  resolved = await resolveSource(shell.kernel, sourceOf(p))
233
248
  } catch (err) {
234
249
  // No window yet (source resolution failed) — windowId honestly absent.
235
- if (!cancelled) setStateSafe({ phase: 'crashed', error: err })
250
+ if (!cancelled) crashAndRelease(err)
236
251
  return
237
252
  }
238
253
  if (cancelled) return
239
254
 
240
255
  const usedHandshake = handshake === 'none' ? 'none' : resolved.handshake
241
256
  const deliveredTarget = refToId(p.node)
257
+ let sandbox: SandboxProfile | null | undefined
258
+ try {
259
+ sandbox =
260
+ typeof p.sandbox === 'function'
261
+ ? p.sandbox({ ...resolved, handshake: usedHandshake })
262
+ : p.sandbox
263
+ } catch (err) {
264
+ if (!cancelled) crashAndRelease(err)
265
+ return
266
+ }
242
267
  const params: MountParams = {
243
268
  host: slotRef.current!.element,
244
269
  url: resolved.url,
245
270
  functionId: resolved.functionId,
246
271
  capabilities: { intents: p.capabilities?.intents ?? [] },
247
272
  targetNodeId: deliveredTarget,
248
- sandbox: p.sandbox,
273
+ sandbox,
249
274
  handshake: usedHandshake,
250
275
  credential: p.credential,
251
276
  }
@@ -277,7 +302,7 @@ export const ViewHost = forwardRef<ViewHostHandle, ViewHostProps>(
277
302
  await mountWith('none', true)
278
303
  return
279
304
  }
280
- setStateSafe({ phase: 'crashed', windowId: windowIdRef.current ?? undefined, error: err })
305
+ crashAndRelease(err)
281
306
  }
282
307
  }
283
308
 
@@ -3,7 +3,7 @@
3
3
  // target-node + close-guard, and the (G5) view-resolution hook.
4
4
  export { ViewStage } from './stage.js'
5
5
  export { ViewHost } from './host.js'
6
- export type { ViewHostProps, ViewHostHandle } from './host.js'
6
+ export type { SandboxPolicy, ViewHostProps, ViewHostHandle } from './host.js'
7
7
  export { useWindows, useWindowList } from './hooks/windows.hook.js'
8
8
  export type { WindowListEntry } from './hooks/windows.hook.js'
9
9
  export { useWindow } from './hooks/window.hook.js'
@@ -13,5 +13,5 @@ export type { ViewsForResult } from './hooks/views-for.hook.js'
13
13
  export { useTargetNode } from './hooks/target-node.hook.js'
14
14
  export { useCloseGuard } from './hooks/close-guard.hook.js'
15
15
  export type { CloseGuard, CloseVerdict } from './hooks/close-guard.hook.js'
16
- export type { ViewDescriptor, ViewSource, Node } from './lifecycle/mount.js'
16
+ export type { ResolvedSource, ViewDescriptor, ViewSource, Node } from './lifecycle/mount.js'
17
17
  export type { WindowState } from './lifecycle/states.js'
@@ -62,7 +62,7 @@ async function resolveViewRef(
62
62
  const node = await kernel.get(toPath(ref))
63
63
  if (node === null) throw new Error(`ViewHost: view not found (${toPath(ref)})`)
64
64
  const props = (node as { id?: string; props?: Record<string, unknown> }).props ?? {}
65
- const url = props.url
65
+ const url = viewUrl(props)
66
66
  if (typeof url !== 'string' || url.length === 0) {
67
67
  throw new Error('ViewHost: resolved view has no url')
68
68
  }
@@ -71,6 +71,36 @@ async function resolveViewRef(
71
71
  return { url, handshake, functionId: id }
72
72
  }
73
73
 
74
+ /** Resolve both the legacy flat url and the canonical Function binding. */
75
+ function viewUrl(props: Record<string, unknown>): string | undefined {
76
+ if (typeof props.url === 'string' && props.url.length > 0) return props.url
77
+
78
+ const direct = remoteUrlOf(props.binding)
79
+ if (direct !== undefined) return direct
80
+
81
+ // Raw graph props can be keyed by the property's canonical graph key rather
82
+ // than its local slug. The binding value remains self-describing.
83
+ for (const value of Object.values(props)) {
84
+ const remoteUrl = remoteUrlOf(value)
85
+ if (remoteUrl !== undefined) return remoteUrl
86
+ }
87
+ return undefined
88
+ }
89
+
90
+ function remoteUrlOf(value: unknown): string | undefined {
91
+ let binding = value
92
+ if (typeof binding === 'string') {
93
+ try {
94
+ binding = JSON.parse(binding)
95
+ } catch {
96
+ return undefined
97
+ }
98
+ }
99
+ if (binding === null || typeof binding !== 'object') return undefined
100
+ const remoteUrl = (binding as { remoteUrl?: unknown }).remoteUrl
101
+ return typeof remoteUrl === 'string' && remoteUrl.length > 0 ? remoteUrl : undefined
102
+ }
103
+
74
104
  function normalizeHandshake(value: unknown): 'shell' | 'none' {
75
105
  return value === 'none' ? 'none' : 'shell'
76
106
  }
@@ -23,6 +23,7 @@ const STAGE_STYLE: CSSProperties = {
23
23
  left: 0,
24
24
  right: 0,
25
25
  bottom: 0,
26
+ zIndex: 20,
26
27
  pointerEvents: 'none',
27
28
  }
28
29