@astrale-os/shell-react 0.2.5 → 0.2.7
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/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/intents/emit-intent.hook.d.ts +17 -0
- package/dist/intents/emit-intent.hook.d.ts.map +1 -0
- package/dist/intents/emit-intent.hook.js +22 -0
- package/dist/intents/index.d.ts +2 -0
- package/dist/intents/index.d.ts.map +1 -1
- package/dist/intents/index.js +1 -0
- package/dist/intents/resolve-intent-target.d.ts +12 -0
- package/dist/intents/resolve-intent-target.d.ts.map +1 -0
- package/dist/intents/resolve-intent-target.js +13 -0
- package/dist/intents/send-intent.hook.d.ts.map +1 -1
- package/dist/intents/send-intent.hook.js +16 -15
- package/dist/window/hooks/views-for.hook.d.ts.map +1 -1
- package/dist/window/hooks/views-for.hook.js +6 -9
- package/package.json +2 -2
- package/src/index.ts +8 -2
- package/src/intents/emit-intent.hook.ts +42 -0
- package/src/intents/index.ts +2 -0
- package/src/intents/resolve-intent-target.ts +20 -0
- package/src/intents/send-intent.hook.ts +21 -23
- package/src/window/hooks/views-for.hook.ts +8 -24
package/dist/index.d.ts
CHANGED
|
@@ -26,8 +26,8 @@ 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
28
|
export type { CloseGuard, CloseVerdict, ViewDescriptor, ViewHostProps, ViewSource, ViewHostHandle, ViewsForResult, WindowListEntry, WindowState, WindowView, } from './window/index.js';
|
|
29
|
-
export { useIntent, useSendIntent } from './intents/index.js';
|
|
30
|
-
export type { IntentCtx, IntentPayload, IntentReply, SendIntent } from './intents/index.js';
|
|
29
|
+
export { useEmitIntent, useIntent, useSendIntent } from './intents/index.js';
|
|
30
|
+
export type { EmitIntent, IntentCtx, IntentPayload, IntentReply, SendIntent, } from './intents/index.js';
|
|
31
31
|
export { AppHost, AppLogo, useApps, useCurrentSpace, useOpenApp, useRoles, useSpace, useSpaces, useUser, } from './application/index.js';
|
|
32
32
|
export type { AppHostFallbackState, AppHostProps, AppLogoProps, AppsResult, CurrentSpaceResult, RolesResult, SpaceResult, SpacesResult, } from './application/index.js';
|
|
33
33
|
export type { ApplicationNode, AppSession, RoleNode, SpaceNode, UserNode } from '@astrale-os/shell';
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAGH,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAA;AAClD,YAAY,EAAE,UAAU,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAA;AACxE,OAAO,EAAE,aAAa,EAAE,eAAe,EAAE,iBAAiB,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAA;AAChG,YAAY,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAA;AAGxD,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAA;AACjE,YAAY,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAA;AAKjD,OAAO,EACL,OAAO,EACP,WAAW,EACX,QAAQ,EACR,OAAO,EACP,SAAS,EACT,OAAO,EACP,QAAQ,EACR,SAAS,GACV,MAAM,kBAAkB,CAAA;AACzB,YAAY,EACV,eAAe,EACf,cAAc,EACd,WAAW,EACX,UAAU,EACV,IAAI,EACJ,UAAU,EACV,WAAW,EACX,UAAU,EACV,gBAAgB,GACjB,MAAM,kBAAkB,CAAA;AACzB,YAAY,EAAE,WAAW,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAA;AAGhF,OAAO,EAAE,SAAS,EAAE,WAAW,EAAE,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAA;AAC/F,YAAY,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAA;AACxD,YAAY,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAA;AACtD,YAAY,EACV,gBAAgB,EAChB,iBAAiB,EACjB,cAAc,EACd,aAAa,EACb,cAAc,EACd,cAAc,EACd,cAAc,EACd,WAAW,EACX,YAAY,EACZ,YAAY,EACZ,oBAAoB,EACpB,mBAAmB,EACnB,eAAe,GAChB,MAAM,mBAAmB,CAAA;AAG1B,OAAO,EACL,IAAI,EACJ,QAAQ,EACR,aAAa,EACb,UAAU,EACV,YAAY,EACZ,WAAW,EACX,WAAW,EACX,WAAW,GACZ,MAAM,uBAAuB,CAAA;AAC9B,YAAY,EACV,SAAS,EACT,aAAa,EACb,UAAU,EACV,WAAW,EACX,cAAc,EACd,aAAa,EACb,WAAW,EACX,SAAS,EACT,QAAQ,EACR,kBAAkB,GACnB,MAAM,uBAAuB,CAAA;AAG9B,OAAO,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAA;AACnE,YAAY,EAAE,SAAS,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAA;AAG1E,OAAO,EACL,aAAa,EACb,aAAa,EACb,WAAW,EACX,SAAS,EACT,aAAa,EACb,UAAU,EACV,QAAQ,EACR,SAAS,GACV,MAAM,mBAAmB,CAAA;AAC1B,YAAY,EACV,UAAU,EACV,YAAY,EACZ,cAAc,EACd,aAAa,EACb,UAAU,EACV,cAAc,EACd,cAAc,EACd,eAAe,EACf,WAAW,EACX,UAAU,GACX,MAAM,mBAAmB,CAAA;AAG1B,OAAO,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAA;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -25,7 +25,7 @@ export { SelectionScope, useSelection } from './selection/index.js';
|
|
|
25
25
|
// ─── window (§10) ────────────────────────────────────────────────────────────
|
|
26
26
|
export { useCloseGuard, useTargetNode, useViewsFor, useWindow, useWindowList, useWindows, ViewHost, ViewStage, } from './window/index.js';
|
|
27
27
|
// ─── intents (§11) ───────────────────────────────────────────────────────────
|
|
28
|
-
export { useIntent, useSendIntent } from './intents/index.js';
|
|
28
|
+
export { useEmitIntent, useIntent, useSendIntent } from './intents/index.js';
|
|
29
29
|
// ─── application — the OS plane (§12) ────────────────────────────────────────
|
|
30
30
|
export { AppHost, AppLogo, useApps, useCurrentSpace, useOpenApp, useRoles, useSpace, useSpaces, useUser, } from './application/index.js';
|
|
31
31
|
// ─── error taxonomy (§1.4) — guards + classes, re-exported from kernel-client ─
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { IntentRegistry } from '@astrale-os/shell';
|
|
2
|
+
import type { IntentPayload } from './envelope.js';
|
|
3
|
+
/** Fire-and-forget intent sender returned by {@link useEmitIntent}. */
|
|
4
|
+
export interface EmitIntent {
|
|
5
|
+
<K extends keyof IntentRegistry>(name: K, payload: IntentPayload<K>,
|
|
6
|
+
/** `to`: `'parent'` (reserved) or a child windowId. Default: the parent, if any. */
|
|
7
|
+
opts?: {
|
|
8
|
+
to?: 'parent' | string;
|
|
9
|
+
}): void;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Return a stable sender for intents that do not need a reply. Emitted messages
|
|
13
|
+
* carry no correlation id, so listeners can handle navigation and notifications
|
|
14
|
+
* without retaining a pending request.
|
|
15
|
+
*/
|
|
16
|
+
export declare function useEmitIntent(): EmitIntent;
|
|
17
|
+
//# sourceMappingURL=emit-intent.hook.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"emit-intent.hook.d.ts","sourceRoot":"","sources":["../../src/intents/emit-intent.hook.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAA;AAIvD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,eAAe,CAAA;AAMlD,uEAAuE;AACvE,MAAM,WAAW,UAAU;IACzB,CAAC,CAAC,SAAS,MAAM,cAAc,EAC7B,IAAI,EAAE,CAAC,EACP,OAAO,EAAE,aAAa,CAAC,CAAC,CAAC;IACzB,oFAAoF;IACpF,IAAI,CAAC,EAAE;QAAE,EAAE,CAAC,EAAE,QAAQ,GAAG,MAAM,CAAA;KAAE,GAChC,IAAI,CAAA;CACR;AAED;;;;GAIG;AACH,wBAAgB,aAAa,IAAI,UAAU,CAgB1C"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { useCallback } from 'react';
|
|
2
|
+
import { useShell } from '../session/shell.hook.js';
|
|
3
|
+
import { intentMessage } from './envelope.js';
|
|
4
|
+
import { resolveIntentTarget } from './resolve-intent-target.js';
|
|
5
|
+
/**
|
|
6
|
+
* Return a stable sender for intents that do not need a reply. Emitted messages
|
|
7
|
+
* carry no correlation id, so listeners can handle navigation and notifications
|
|
8
|
+
* without retaining a pending request.
|
|
9
|
+
*/
|
|
10
|
+
export function useEmitIntent() {
|
|
11
|
+
const shell = useShell();
|
|
12
|
+
return useCallback((name, payload, opts) => {
|
|
13
|
+
const target = resolveIntentTarget(shell, opts?.to);
|
|
14
|
+
if (target.kind === 'invalid')
|
|
15
|
+
throw new Error(`useEmitIntent: ${target.reason}`);
|
|
16
|
+
const message = intentMessage(name, payload);
|
|
17
|
+
if (target.kind === 'parent')
|
|
18
|
+
shell.parent.send(message);
|
|
19
|
+
else
|
|
20
|
+
shell.children.send(target.windowId, message);
|
|
21
|
+
}, [shell]);
|
|
22
|
+
}
|
package/dist/intents/index.d.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
export { useIntent } from './listen-intent.hook.js';
|
|
2
2
|
export type { IntentCtx } from './listen-intent.hook.js';
|
|
3
|
+
export { useEmitIntent } from './emit-intent.hook.js';
|
|
4
|
+
export type { EmitIntent } from './emit-intent.hook.js';
|
|
3
5
|
export { useSendIntent } from './send-intent.hook.js';
|
|
4
6
|
export type { SendIntent } from './send-intent.hook.js';
|
|
5
7
|
export type { IntentPayload, IntentReply } from './envelope.js';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/intents/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAA;AACnD,YAAY,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAA;AACxD,OAAO,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAA;AACrD,YAAY,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAA;AACvD,YAAY,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,eAAe,CAAA"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/intents/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAA;AACnD,YAAY,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAA;AACxD,OAAO,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAA;AACrD,YAAY,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAA;AACvD,OAAO,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAA;AACrD,YAAY,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAA;AACvD,YAAY,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,eAAe,CAAA"}
|
package/dist/intents/index.js
CHANGED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { Shell } from '@astrale-os/shell';
|
|
2
|
+
export type IntentTarget = {
|
|
3
|
+
readonly kind: 'parent';
|
|
4
|
+
} | {
|
|
5
|
+
readonly kind: 'child';
|
|
6
|
+
readonly windowId: string;
|
|
7
|
+
} | {
|
|
8
|
+
readonly kind: 'invalid';
|
|
9
|
+
readonly reason: string;
|
|
10
|
+
};
|
|
11
|
+
export declare function resolveIntentTarget(shell: Shell, to: string | undefined): IntentTarget;
|
|
12
|
+
//# sourceMappingURL=resolve-intent-target.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resolve-intent-target.d.ts","sourceRoot":"","sources":["../../src/intents/resolve-intent-target.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAA;AAE9C,MAAM,MAAM,YAAY,GACpB;IAAE,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAA;CAAE,GAC3B;IAAE,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;IAAC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,GACrD;IAAE,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAA;AAEzD,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,KAAK,EAAE,EAAE,EAAE,MAAM,GAAG,SAAS,GAAG,YAAY,CAYtF"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export function resolveIntentTarget(shell, to) {
|
|
2
|
+
if (to === undefined) {
|
|
3
|
+
return shell.parent !== null
|
|
4
|
+
? { kind: 'parent' }
|
|
5
|
+
: { kind: 'invalid', reason: 'no target — pass opts.to (a child windowId) from a root shell' };
|
|
6
|
+
}
|
|
7
|
+
if (to === 'parent') {
|
|
8
|
+
return shell.parent !== null
|
|
9
|
+
? { kind: 'parent' }
|
|
10
|
+
: { kind: 'invalid', reason: "opts.to 'parent' but this shell has no parent channel" };
|
|
11
|
+
}
|
|
12
|
+
return { kind: 'child', windowId: to };
|
|
13
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"send-intent.hook.d.ts","sourceRoot":"","sources":["../../src/intents/send-intent.hook.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAiB,cAAc,
|
|
1
|
+
{"version":3,"file":"send-intent.hook.d.ts","sourceRoot":"","sources":["../../src/intents/send-intent.hook.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAiB,cAAc,EAAE,MAAM,mBAAmB,CAAA;AAWtE,OAAO,KAAK,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,eAAe,CAAA;AAM/D,0DAA0D;AAC1D,MAAM,WAAW,UAAU;IACzB,CAAC,CAAC,SAAS,MAAM,cAAc,EAC7B,IAAI,EAAE,CAAC,EACP,OAAO,EAAE,aAAa,CAAC,CAAC,CAAC;IACzB,oFAAoF;IACpF,IAAI,CAAC,EAAE;QAAE,EAAE,CAAC,EAAE,QAAQ,GAAG,MAAM,CAAA;KAAE,GAChC,OAAO,CAAC,WAAW,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,CAAA;CAClC;AAED,wBAAgB,aAAa,IAAI,UAAU,CAiF1C"}
|
|
@@ -8,19 +8,7 @@
|
|
|
8
8
|
import { useCallback, useEffect, useRef } from 'react';
|
|
9
9
|
import { useShell } from '../session/shell.hook.js';
|
|
10
10
|
import { INTENT_REPLY, NOOP, intentMessage } from './envelope.js';
|
|
11
|
-
|
|
12
|
-
if (to === undefined) {
|
|
13
|
-
return shell.parent !== null
|
|
14
|
-
? { kind: 'parent' }
|
|
15
|
-
: { kind: 'invalid', reason: 'no target — pass opts.to (a child windowId) from a root shell' };
|
|
16
|
-
}
|
|
17
|
-
if (to === 'parent') {
|
|
18
|
-
return shell.parent !== null
|
|
19
|
-
? { kind: 'parent' }
|
|
20
|
-
: { kind: 'invalid', reason: "opts.to 'parent' but this shell has no parent channel" };
|
|
21
|
-
}
|
|
22
|
-
return { kind: 'child', windowId: to };
|
|
23
|
-
}
|
|
11
|
+
import { resolveIntentTarget } from './resolve-intent-target.js';
|
|
24
12
|
export function useSendIntent() {
|
|
25
13
|
const shell = useShell();
|
|
26
14
|
// Unmount settlers for in-flight sends, so a pending reply resolves on teardown.
|
|
@@ -34,14 +22,14 @@ export function useSendIntent() {
|
|
|
34
22
|
};
|
|
35
23
|
}, []);
|
|
36
24
|
return useCallback((name, payload, opts) => {
|
|
37
|
-
const target =
|
|
25
|
+
const target = resolveIntentTarget(shell, opts?.to);
|
|
38
26
|
if (target.kind === 'invalid') {
|
|
39
27
|
return Promise.reject(new Error(`useSendIntent: ${target.reason}`));
|
|
40
28
|
}
|
|
41
29
|
const correlationId = crypto.randomUUID();
|
|
42
30
|
const message = intentMessage(name, payload, correlationId);
|
|
43
31
|
const pending = pendingRef.current;
|
|
44
|
-
return new Promise((resolve) => {
|
|
32
|
+
return new Promise((resolve, reject) => {
|
|
45
33
|
let done = false;
|
|
46
34
|
let offReply = NOOP;
|
|
47
35
|
let offClose = NOOP;
|
|
@@ -55,12 +43,25 @@ export function useSendIntent() {
|
|
|
55
43
|
pending.delete(onUnmount);
|
|
56
44
|
resolve(value);
|
|
57
45
|
}
|
|
46
|
+
function fail(error) {
|
|
47
|
+
if (done)
|
|
48
|
+
return;
|
|
49
|
+
done = true;
|
|
50
|
+
offReply();
|
|
51
|
+
offClose();
|
|
52
|
+
pending.delete(onUnmount);
|
|
53
|
+
reject(error);
|
|
54
|
+
}
|
|
58
55
|
const onReply = (m) => {
|
|
59
56
|
if (m.envelope.name !== INTENT_REPLY)
|
|
60
57
|
return;
|
|
61
58
|
const reply = m.envelope.payload;
|
|
62
59
|
if (reply.correlationId !== correlationId)
|
|
63
60
|
return;
|
|
61
|
+
if (reply.error) {
|
|
62
|
+
fail(new Error(reply.error.message));
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
64
65
|
settle(reply.result);
|
|
65
66
|
};
|
|
66
67
|
pending.add(onUnmount);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"views-for.hook.d.ts","sourceRoot":"","sources":["../../../src/window/hooks/views-for.hook.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,iCAAiC,CAAA;AAW/D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,qCAAqC,CAAA;AACpE,OAAO,KAAK,EAAE,IAAI,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAA;AAKjE,MAAM,WAAW,cAAe,SAAQ,SAAS;IAC/C,QAAQ,CAAC,KAAK,EAAE,SAAS,cAAc,EAAE,CAAA;CAC1C;
|
|
1
|
+
{"version":3,"file":"views-for.hook.d.ts","sourceRoot":"","sources":["../../../src/window/hooks/views-for.hook.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,iCAAiC,CAAA;AAW/D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,qCAAqC,CAAA;AACpE,OAAO,KAAK,EAAE,IAAI,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAA;AAKjE,MAAM,WAAW,cAAe,SAAQ,SAAS;IAC/C,QAAQ,CAAC,KAAK,EAAE,SAAS,cAAc,EAAE,CAAA;CAC1C;AAID,wBAAgB,WAAW,CAAC,IAAI,EAAE,IAAI,GAAG,QAAQ,GAAG,IAAI,GAAG,SAAS,GAAG,cAAc,CAqBpF"}
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { K } from '@astrale-os/kernel-core';
|
|
2
1
|
/**
|
|
3
2
|
* useViewsFor (API §10) — "what Views can render this node", as mountable
|
|
4
3
|
* `ViewDescriptor`s for the open-with menu. Backed one-shot (`live:false`) by the
|
|
@@ -7,14 +6,12 @@ import { K } from '@astrale-os/kernel-core';
|
|
|
7
6
|
*/
|
|
8
7
|
import { useMemo } from 'react';
|
|
9
8
|
import { useAsyncResource } from '../../graph/async.js';
|
|
10
|
-
import {
|
|
11
|
-
/** The `View.resolve` wire address — the qualified static-method slug from the schema. */
|
|
12
|
-
const VIEW_RESOLVE_METHOD = K.View.resolve.path.method.raw;
|
|
9
|
+
import { useShell } from '../../session/shell.hook.js';
|
|
13
10
|
const NO_VIEWS = Object.freeze([]);
|
|
14
11
|
export function useViewsFor(node) {
|
|
15
|
-
const
|
|
12
|
+
const shell = useShell();
|
|
16
13
|
const target = resolvePath(node);
|
|
17
|
-
const res = useAsyncResource(target === null ? null : () => resolveViews(
|
|
14
|
+
const res = useAsyncResource(target === null ? null : () => resolveViews(shell, target), NO_VIEWS, [shell, target]);
|
|
18
15
|
return useMemo(() => ({
|
|
19
16
|
views: res.data,
|
|
20
17
|
pending: res.pending,
|
|
@@ -25,9 +22,9 @@ export function useViewsFor(node) {
|
|
|
25
22
|
}), [res.data, res.pending, res.error, res.refetch]);
|
|
26
23
|
}
|
|
27
24
|
/** Dispatch `View.resolve` and project its rows to mountable `ViewDescriptor`s. */
|
|
28
|
-
async function resolveViews(
|
|
29
|
-
const rows =
|
|
30
|
-
if (rows
|
|
25
|
+
async function resolveViews(shell, node) {
|
|
26
|
+
const rows = await shell.views.resolve(node);
|
|
27
|
+
if (rows.length === 0)
|
|
31
28
|
return NO_VIEWS;
|
|
32
29
|
return rows.map((r) => ({ id: r.id, path: r.path, url: r.url, handshake: r.handshake }));
|
|
33
30
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@astrale-os/shell-react",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.7",
|
|
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.
|
|
47
|
+
"@astrale-os/shell": "0.3.5"
|
|
48
48
|
},
|
|
49
49
|
"peerDependencies": {
|
|
50
50
|
"@astrale-os/shell": ">=0.2.0 <1.0.0",
|
package/src/index.ts
CHANGED
|
@@ -117,8 +117,14 @@ export type {
|
|
|
117
117
|
} from './window/index.js'
|
|
118
118
|
|
|
119
119
|
// ─── intents (§11) ───────────────────────────────────────────────────────────
|
|
120
|
-
export { useIntent, useSendIntent } from './intents/index.js'
|
|
121
|
-
export type {
|
|
120
|
+
export { useEmitIntent, useIntent, useSendIntent } from './intents/index.js'
|
|
121
|
+
export type {
|
|
122
|
+
EmitIntent,
|
|
123
|
+
IntentCtx,
|
|
124
|
+
IntentPayload,
|
|
125
|
+
IntentReply,
|
|
126
|
+
SendIntent,
|
|
127
|
+
} from './intents/index.js'
|
|
122
128
|
|
|
123
129
|
// ─── application — the OS plane (§12) ────────────────────────────────────────
|
|
124
130
|
export {
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import type { IntentRegistry } from '@astrale-os/shell'
|
|
2
|
+
|
|
3
|
+
import { useCallback } from 'react'
|
|
4
|
+
|
|
5
|
+
import type { IntentPayload } from './envelope.js'
|
|
6
|
+
|
|
7
|
+
import { useShell } from '../session/shell.hook.js'
|
|
8
|
+
import { intentMessage } from './envelope.js'
|
|
9
|
+
import { resolveIntentTarget } from './resolve-intent-target.js'
|
|
10
|
+
|
|
11
|
+
/** Fire-and-forget intent sender returned by {@link useEmitIntent}. */
|
|
12
|
+
export interface EmitIntent {
|
|
13
|
+
<K extends keyof IntentRegistry>(
|
|
14
|
+
name: K,
|
|
15
|
+
payload: IntentPayload<K>,
|
|
16
|
+
/** `to`: `'parent'` (reserved) or a child windowId. Default: the parent, if any. */
|
|
17
|
+
opts?: { to?: 'parent' | string },
|
|
18
|
+
): void
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Return a stable sender for intents that do not need a reply. Emitted messages
|
|
23
|
+
* carry no correlation id, so listeners can handle navigation and notifications
|
|
24
|
+
* without retaining a pending request.
|
|
25
|
+
*/
|
|
26
|
+
export function useEmitIntent(): EmitIntent {
|
|
27
|
+
const shell = useShell()
|
|
28
|
+
return useCallback(
|
|
29
|
+
<K extends keyof IntentRegistry>(
|
|
30
|
+
name: K,
|
|
31
|
+
payload: IntentPayload<K>,
|
|
32
|
+
opts?: { to?: 'parent' | string },
|
|
33
|
+
): void => {
|
|
34
|
+
const target = resolveIntentTarget(shell, opts?.to)
|
|
35
|
+
if (target.kind === 'invalid') throw new Error(`useEmitIntent: ${target.reason}`)
|
|
36
|
+
const message = intentMessage(name, payload)
|
|
37
|
+
if (target.kind === 'parent') shell.parent!.send(message)
|
|
38
|
+
else shell.children.send(target.windowId, message)
|
|
39
|
+
},
|
|
40
|
+
[shell],
|
|
41
|
+
)
|
|
42
|
+
}
|
package/src/intents/index.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
// intents — API §11 (useIntent, useSendIntent). See SHELL_REACT_API.md.
|
|
2
2
|
export { useIntent } from './listen-intent.hook.js'
|
|
3
3
|
export type { IntentCtx } from './listen-intent.hook.js'
|
|
4
|
+
export { useEmitIntent } from './emit-intent.hook.js'
|
|
5
|
+
export type { EmitIntent } from './emit-intent.hook.js'
|
|
4
6
|
export { useSendIntent } from './send-intent.hook.js'
|
|
5
7
|
export type { SendIntent } from './send-intent.hook.js'
|
|
6
8
|
export type { IntentPayload, IntentReply } from './envelope.js'
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { Shell } from '@astrale-os/shell'
|
|
2
|
+
|
|
3
|
+
export type IntentTarget =
|
|
4
|
+
| { readonly kind: 'parent' }
|
|
5
|
+
| { readonly kind: 'child'; readonly windowId: string }
|
|
6
|
+
| { readonly kind: 'invalid'; readonly reason: string }
|
|
7
|
+
|
|
8
|
+
export function resolveIntentTarget(shell: Shell, to: string | undefined): IntentTarget {
|
|
9
|
+
if (to === undefined) {
|
|
10
|
+
return shell.parent !== null
|
|
11
|
+
? { kind: 'parent' }
|
|
12
|
+
: { kind: 'invalid', reason: 'no target — pass opts.to (a child windowId) from a root shell' }
|
|
13
|
+
}
|
|
14
|
+
if (to === 'parent') {
|
|
15
|
+
return shell.parent !== null
|
|
16
|
+
? { kind: 'parent' }
|
|
17
|
+
: { kind: 'invalid', reason: "opts.to 'parent' but this shell has no parent channel" }
|
|
18
|
+
}
|
|
19
|
+
return { kind: 'child', windowId: to }
|
|
20
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { IntentMessage, IntentRegistry
|
|
1
|
+
import type { IntentMessage, IntentRegistry } from '@astrale-os/shell'
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* useSendIntent (API §11) — a stable typed sender. Each call attaches a
|
|
@@ -13,6 +13,7 @@ import type { IntentPayload, IntentReply } from './envelope.js'
|
|
|
13
13
|
|
|
14
14
|
import { useShell } from '../session/shell.hook.js'
|
|
15
15
|
import { INTENT_REPLY, NOOP, intentMessage } from './envelope.js'
|
|
16
|
+
import { resolveIntentTarget } from './resolve-intent-target.js'
|
|
16
17
|
|
|
17
18
|
/** The typed sender returned by {@link useSendIntent}. */
|
|
18
19
|
export interface SendIntent {
|
|
@@ -24,25 +25,6 @@ export interface SendIntent {
|
|
|
24
25
|
): Promise<IntentReply<K> | void>
|
|
25
26
|
}
|
|
26
27
|
|
|
27
|
-
type Target =
|
|
28
|
-
| { readonly kind: 'parent' }
|
|
29
|
-
| { readonly kind: 'child'; readonly windowId: string }
|
|
30
|
-
| { readonly kind: 'invalid'; readonly reason: string }
|
|
31
|
-
|
|
32
|
-
function resolveTarget(shell: Shell, to: string | undefined): Target {
|
|
33
|
-
if (to === undefined) {
|
|
34
|
-
return shell.parent !== null
|
|
35
|
-
? { kind: 'parent' }
|
|
36
|
-
: { kind: 'invalid', reason: 'no target — pass opts.to (a child windowId) from a root shell' }
|
|
37
|
-
}
|
|
38
|
-
if (to === 'parent') {
|
|
39
|
-
return shell.parent !== null
|
|
40
|
-
? { kind: 'parent' }
|
|
41
|
-
: { kind: 'invalid', reason: "opts.to 'parent' but this shell has no parent channel" }
|
|
42
|
-
}
|
|
43
|
-
return { kind: 'child', windowId: to }
|
|
44
|
-
}
|
|
45
|
-
|
|
46
28
|
export function useSendIntent(): SendIntent {
|
|
47
29
|
const shell = useShell()
|
|
48
30
|
// Unmount settlers for in-flight sends, so a pending reply resolves on teardown.
|
|
@@ -61,7 +43,7 @@ export function useSendIntent(): SendIntent {
|
|
|
61
43
|
payload: IntentPayload<K>,
|
|
62
44
|
opts?: { to?: 'parent' | string },
|
|
63
45
|
): Promise<IntentReply<K> | void> => {
|
|
64
|
-
const target =
|
|
46
|
+
const target = resolveIntentTarget(shell, opts?.to)
|
|
65
47
|
if (target.kind === 'invalid') {
|
|
66
48
|
return Promise.reject(new Error(`useSendIntent: ${target.reason}`))
|
|
67
49
|
}
|
|
@@ -69,7 +51,7 @@ export function useSendIntent(): SendIntent {
|
|
|
69
51
|
const message = intentMessage(name, payload, correlationId)
|
|
70
52
|
const pending = pendingRef.current
|
|
71
53
|
|
|
72
|
-
return new Promise<IntentReply<K> | void>((resolve) => {
|
|
54
|
+
return new Promise<IntentReply<K> | void>((resolve, reject) => {
|
|
73
55
|
let done = false
|
|
74
56
|
let offReply = NOOP
|
|
75
57
|
let offClose = NOOP
|
|
@@ -82,10 +64,26 @@ export function useSendIntent(): SendIntent {
|
|
|
82
64
|
pending.delete(onUnmount)
|
|
83
65
|
resolve(value)
|
|
84
66
|
}
|
|
67
|
+
function fail(error: Error): void {
|
|
68
|
+
if (done) return
|
|
69
|
+
done = true
|
|
70
|
+
offReply()
|
|
71
|
+
offClose()
|
|
72
|
+
pending.delete(onUnmount)
|
|
73
|
+
reject(error)
|
|
74
|
+
}
|
|
85
75
|
const onReply = (m: IntentMessage): void => {
|
|
86
76
|
if (m.envelope.name !== INTENT_REPLY) return
|
|
87
|
-
const reply = m.envelope.payload as {
|
|
77
|
+
const reply = m.envelope.payload as {
|
|
78
|
+
correlationId: string
|
|
79
|
+
result?: unknown
|
|
80
|
+
error?: { message: string }
|
|
81
|
+
}
|
|
88
82
|
if (reply.correlationId !== correlationId) return
|
|
83
|
+
if (reply.error) {
|
|
84
|
+
fail(new Error(reply.error.message))
|
|
85
|
+
return
|
|
86
|
+
}
|
|
89
87
|
settle(reply.result as IntentReply<K>)
|
|
90
88
|
}
|
|
91
89
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { PathLike } from '@astrale-os/kernel-client/graph'
|
|
2
|
+
import type { Shell } from '@astrale-os/shell'
|
|
2
3
|
|
|
3
|
-
import { K } from '@astrale-os/kernel-core'
|
|
4
4
|
/**
|
|
5
5
|
* useViewsFor (API §10) — "what Views can render this node", as mountable
|
|
6
6
|
* `ViewDescriptor`s for the open-with menu. Backed one-shot (`live:false`) by the
|
|
@@ -13,35 +13,22 @@ import type { ReadState } from '../../graph/memory/read/envelope.js'
|
|
|
13
13
|
import type { Node, ViewDescriptor } from '../lifecycle/mount.js'
|
|
14
14
|
|
|
15
15
|
import { useAsyncResource } from '../../graph/async.js'
|
|
16
|
-
import {
|
|
16
|
+
import { useShell } from '../../session/shell.hook.js'
|
|
17
17
|
|
|
18
18
|
export interface ViewsForResult extends ReadState {
|
|
19
19
|
readonly views: readonly ViewDescriptor[]
|
|
20
20
|
}
|
|
21
21
|
|
|
22
|
-
/** The `View.resolve` wire address — the qualified static-method slug from the schema. */
|
|
23
|
-
const VIEW_RESOLVE_METHOD = K.View.resolve.path.method.raw
|
|
24
|
-
|
|
25
|
-
/** The kernel `View.resolve` row shape (id/path/url/name?/handshake?/origin). */
|
|
26
|
-
interface ViewOut {
|
|
27
|
-
readonly id: string
|
|
28
|
-
readonly path: string
|
|
29
|
-
readonly url: string
|
|
30
|
-
readonly name?: string
|
|
31
|
-
readonly handshake?: 'shell' | 'none'
|
|
32
|
-
readonly origin: 'self' | 'class'
|
|
33
|
-
}
|
|
34
|
-
|
|
35
22
|
const NO_VIEWS: readonly ViewDescriptor[] = Object.freeze([])
|
|
36
23
|
|
|
37
24
|
export function useViewsFor(node: Node | PathLike | null | undefined): ViewsForResult {
|
|
38
|
-
const
|
|
25
|
+
const shell = useShell()
|
|
39
26
|
const target = resolvePath(node)
|
|
40
27
|
|
|
41
28
|
const res = useAsyncResource<readonly ViewDescriptor[]>(
|
|
42
|
-
target === null ? null : () => resolveViews(
|
|
29
|
+
target === null ? null : () => resolveViews(shell, target),
|
|
43
30
|
NO_VIEWS,
|
|
44
|
-
[
|
|
31
|
+
[shell, target],
|
|
45
32
|
)
|
|
46
33
|
|
|
47
34
|
return useMemo<ViewsForResult>(
|
|
@@ -58,12 +45,9 @@ export function useViewsFor(node: Node | PathLike | null | undefined): ViewsForR
|
|
|
58
45
|
}
|
|
59
46
|
|
|
60
47
|
/** Dispatch `View.resolve` and project its rows to mountable `ViewDescriptor`s. */
|
|
61
|
-
async function resolveViews(
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
): Promise<readonly ViewDescriptor[]> {
|
|
65
|
-
const rows = (await kernel.call(VIEW_RESOLVE_METHOD, { node })) as ViewOut[] | null | undefined
|
|
66
|
-
if (rows === null || rows === undefined || rows.length === 0) return NO_VIEWS
|
|
48
|
+
async function resolveViews(shell: Shell, node: string): Promise<readonly ViewDescriptor[]> {
|
|
49
|
+
const rows = await shell.views.resolve(node)
|
|
50
|
+
if (rows.length === 0) return NO_VIEWS
|
|
67
51
|
return rows.map((r) => ({ id: r.id, path: r.path, url: r.url, handshake: r.handshake }))
|
|
68
52
|
}
|
|
69
53
|
|