@fungi.computer/agar 0.1.0 → 0.2.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/dist/agent-message-meta.d.ts +15 -15
- package/dist/agent-message-meta.js +5 -5
- package/dist/agents.d.ts +86 -13
- package/dist/agents.js +48 -25
- package/dist/apps.js +4 -4
- package/dist/backend-definition.d.ts +41 -4
- package/dist/backend-definition.js +70 -10
- package/dist/backend.js +4 -4
- package/dist/channels.js +4 -4
- package/dist/computer-contracts.d.ts +15 -15
- package/dist/computer-contracts.js +14 -14
- package/dist/computer.js +16 -16
- package/dist/game-servers.js +2 -2
- package/dist/git-rpc.d.ts +23 -23
- package/dist/git-rpc.js +14 -16
- package/dist/git.d.ts +9 -9
- package/dist/git.js +8 -8
- package/dist/host.d.ts +3 -1
- package/dist/host.js +5 -1
- package/dist/index.d.ts +5 -4
- package/dist/index.js +3 -2
- package/dist/inference.js +2 -1
- package/dist/internal/app-session.js +37 -1
- package/dist/internal/protocol.d.ts +4 -4
- package/dist/internal/schemas.d.ts +33 -7
- package/dist/internal/schemas.js +107 -16
- package/dist/marketplace.js +7 -5
- package/dist/media-request-id.d.ts +1 -1
- package/dist/media-request-id.js +2 -2
- package/dist/media.js +2 -2
- package/dist/method-presentation.d.ts +7 -0
- package/dist/method-presentation.js +14 -0
- package/dist/method-refusal.d.ts +5 -5
- package/dist/method-refusal.js +4 -4
- package/dist/react.d.ts +14 -2
- package/dist/react.js +40 -4
- package/dist/read-copy.d.ts +1 -1
- package/dist/read-copy.js +3 -6
- package/dist/storage.js +1 -1
- package/dist/test-kit/git.js +19 -2
- package/dist/window.d.ts +64 -0
- package/dist/window.js +31 -0
- package/docs/api/backend-definition.md +83 -9
- package/docs/api/backend.md +44 -38
- package/docs/api/git-rpc.md +35 -35
- package/docs/api/git.md +66 -80
- package/docs/api/index.md +775 -128
- package/docs/api/media-request-id.md +1 -1
- package/docs/api/method-presentation.md +21 -0
- package/docs/api/modules.md +1 -0
- package/docs/api/react.md +10 -4
- package/package.json +10 -5
package/dist/index.d.ts
CHANGED
|
@@ -4,8 +4,8 @@ export { createCapabilityRegistry, defineCapability } from "./capabilities.js";
|
|
|
4
4
|
export { pending, pendingSchema } from "./pending.js";
|
|
5
5
|
export type { Pending } from "./pending.js";
|
|
6
6
|
export type { CapabilityContract, CapabilityDirectory, CapabilityRegistry, } from "./capabilities.js";
|
|
7
|
-
export { agentList, agentManage, agentRename, agentSessionRead, agentSessionWrite, agentToolPolicy, createAppAgent, renameAppAgent, appAgentCreateInputSchema, appAgentRenameInputSchema, appAgentSchema, appAgentToolPolicyResultSchema, appAgentToolPolicyStatusSchema, listAppAgents, openAppAgent, } from "./agents.js";
|
|
8
|
-
export type { AppAgent, AppAgentCreateHandle, AppAgentCreateInput, AppAgentDirectoryHandle, AppAgentListHandle, AppAgentRenameHandle, AppAgentRenameInput, AppAgentToolPolicyHandle, AppAgentToolPolicyResult, AppAgentToolPolicyStatus, AppAgentTransport, AppAgentTransportHandle, } from "./agents.js";
|
|
7
|
+
export { agentAvatar, agentList, agentManage, agentRename, agentSessionRead, agentSessionWrite, agentToolPolicy, createAppAgent, renameAppAgent, setAppAgentAvatar, appAgentAvatarInputSchema, appAgentAvatarSchema, appAgentCreateInputSchema, appAgentRenameInputSchema, appAgentSchema, appAgentToolPolicyResultSchema, appAgentToolPolicyStatusSchema, listAppAgents, openAppAgent, } from "./agents.js";
|
|
8
|
+
export type { AppAgent, AppAgentAvatarHandle, AppAgentAvatarInput, AppAgentCreateHandle, AppAgentCreateInput, AppAgentDirectoryHandle, AppAgentListHandle, AppAgentRenameHandle, AppAgentRenameInput, AppAgentToolPolicyHandle, AppAgentToolPolicyResult, AppAgentToolPolicyStatus, AppAgentTransport, AppAgentTransportHandle, } from "./agents.js";
|
|
9
9
|
export { mediaGeneration, mediaGenerationFailureSchema, mediaGenerationReferenceSchema, mediaGenerationSelectionSchema, mediaModelSchema, openMediaGeneration, } from "./media.js";
|
|
10
10
|
export type { MediaGenerationHandle, MediaGenerationAttempt, MediaGenerationAttemptHandle, MediaGenerationFailure, MediaGenerationInput, MediaGenerationOutcome, MediaGenerationReference, MediaGenerationResult, MediaGenerationSelection, MediaModel, MediaOutputFormat, MediaGenerationSession, } from "./media.js";
|
|
11
11
|
export type { AppCommand, AppCommandHandler, AppCommandInvocation, AppCommands, } from "./commands.js";
|
|
@@ -25,7 +25,8 @@ export type { AppRoot } from "./root.js";
|
|
|
25
25
|
export type { AgentSessionSelection } from "./ui.js";
|
|
26
26
|
export type { Disposer, ReadonlyStore } from "./store.js";
|
|
27
27
|
export type { AppBrowserAction, AppBrowserActionOutcome, AppBrowserActionRefusal, AgentAccessOperation, AppPermissionOutcome, AppPermissionRequest, AppStatusAction, AppStatusActionHandler, AppStatusIcon, AppStatusItem, AppStatusOption, AppStatusTone, ComputerAccessOperation, FileNavigationInput, HostUi, ToastInput, } from "./ui.js";
|
|
28
|
-
export {
|
|
29
|
-
export
|
|
28
|
+
export { resultRowSchemaV1, type ResultRow } from "@fungi.computer/gills";
|
|
29
|
+
export { APP_NAVIGATION_CONTRACT_VERSION, APP_NAVIGATION_CRUMBS_MAX, APP_NAVIGATION_LABEL_MAX, APP_NAVIGATION_MENU_MAX, APP_NAVIGATION_PRIMARY_MAX, APP_NAVIGATION_SIBLINGS_MAX, APP_NAVIGATION_STATUS_MAX, APP_NAVIGATION_TITLE_MAX, inertHostNavigation, navigationActivationAllowed, } from "./window.js";
|
|
30
|
+
export type { AppBackHandler, AppCrumb, AppHeaderCommand, AppHeaderStatus, AppNavigationActivation, AppNavigationActivationHandler, AppNavigationState, AppWindow, BeforeAppClose, HostNavigation, } from "./window.js";
|
|
30
31
|
export { appStorage, AppStorageError } from "./storage.js";
|
|
31
32
|
export type { AppStorage, AppStorageWatch, AppStorageWrite, AppStorageReset, ResolvedAppStorage, } from "./storage.js";
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export { FungiAuthorSchema, FungiDelegationReplySchema, FungiMessageMetaSchema, messageAuthor, messageDelegationReply, authorMeta, } from "./agent-message-meta.js";
|
|
2
2
|
export { createCapabilityRegistry, defineCapability } from "./capabilities.js";
|
|
3
3
|
export { pending, pendingSchema } from "./pending.js";
|
|
4
|
-
export { agentList, agentManage, agentRename, agentSessionRead, agentSessionWrite, agentToolPolicy, createAppAgent, renameAppAgent, appAgentCreateInputSchema, appAgentRenameInputSchema, appAgentSchema, appAgentToolPolicyResultSchema, appAgentToolPolicyStatusSchema, listAppAgents, openAppAgent, } from "./agents.js";
|
|
4
|
+
export { agentAvatar, agentList, agentManage, agentRename, agentSessionRead, agentSessionWrite, agentToolPolicy, createAppAgent, renameAppAgent, setAppAgentAvatar, appAgentAvatarInputSchema, appAgentAvatarSchema, appAgentCreateInputSchema, appAgentRenameInputSchema, appAgentSchema, appAgentToolPolicyResultSchema, appAgentToolPolicyStatusSchema, listAppAgents, openAppAgent, } from "./agents.js";
|
|
5
5
|
export { mediaGeneration, mediaGenerationFailureSchema, mediaGenerationReferenceSchema, mediaGenerationSelectionSchema, mediaModelSchema, openMediaGeneration, } from "./media.js";
|
|
6
6
|
export { registerCatalog } from "./commands.js";
|
|
7
7
|
export { connectApp } from "./connect.js";
|
|
@@ -11,5 +11,6 @@ export { AppError } from "./errors.js";
|
|
|
11
11
|
export { statusActionAllowed, statusText } from "./status.js";
|
|
12
12
|
export { APP_BROWSER_SAVE_MAX_BYTES, APP_BROWSER_COPY_MAX_BYTES, APP_STATUS_ITEMS_MAX, APP_STATUS_OPTIONS_MAX, APP_STATUS_TEXT_MAX, } from "./ui.js";
|
|
13
13
|
export { parseStatusAction as parseAppStatusAction, parseStatusItems as parseAppStatusItems, } from "./internal/schemas.js";
|
|
14
|
-
export {
|
|
14
|
+
export { resultRowSchemaV1 } from "@fungi.computer/gills";
|
|
15
|
+
export { APP_NAVIGATION_CONTRACT_VERSION, APP_NAVIGATION_CRUMBS_MAX, APP_NAVIGATION_LABEL_MAX, APP_NAVIGATION_MENU_MAX, APP_NAVIGATION_PRIMARY_MAX, APP_NAVIGATION_SIBLINGS_MAX, APP_NAVIGATION_STATUS_MAX, APP_NAVIGATION_TITLE_MAX, inertHostNavigation, navigationActivationAllowed, } from "./window.js";
|
|
15
16
|
export { appStorage, AppStorageError } from "./storage.js";
|
package/dist/inference.js
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { defineCapability } from "./capabilities.js";
|
|
2
2
|
import { rpcFetchHandleSchema, rpcHandleSchema, rpcMethod, } from "./internal/rpc-contract.js";
|
|
3
|
+
import * as z from "zod";
|
|
3
4
|
import * as Schema from "effect/Schema";
|
|
4
5
|
import { AppError } from "./errors.js";
|
|
5
6
|
import { reportCatchFailure } from "./failure.js";
|
|
6
|
-
export const llmGeneration = defineCapability("llm.generation", 1, rpcHandleSchema({ open: rpcMethod(rpcFetchHandleSchema) }));
|
|
7
|
+
export const llmGeneration = defineCapability("llm.generation", 1, z.lazy(() => rpcHandleSchema({ open: rpcMethod(rpcFetchHandleSchema) })));
|
|
7
8
|
const baseURL = "https://inference.fungi.invalid/v1";
|
|
8
9
|
const decodeResponse = Schema.decodeUnknownSync(Schema.instanceOf(Response));
|
|
9
10
|
/** OpenAI-compatible transport; no Agent, Session, host URL or credentials are exposed. */
|
|
@@ -5,6 +5,7 @@ import * as Schema from "effect/Schema";
|
|
|
5
5
|
*/
|
|
6
6
|
import { newMessagePortRpcSession, RpcStub, RpcTarget, } from "@fungi.computer/capnweb";
|
|
7
7
|
import { statusActionAllowed } from "../status.js";
|
|
8
|
+
import { navigationActivationAllowed } from "../window.js";
|
|
8
9
|
import { AppError } from "../errors.js";
|
|
9
10
|
import { reportCatchFailure } from "../failure.js";
|
|
10
11
|
import { createLocalBindingState, createReservedChordState, installBrowserBridge, } from "./browser-bridge.js";
|
|
@@ -13,7 +14,7 @@ import { createCapabilityDirectory, disposeRpcValue, } from "./capabilities.js";
|
|
|
13
14
|
import { createAppCommands } from "./commands.js";
|
|
14
15
|
import { APP_PROTOCOL_VERSION, } from "./protocol.js";
|
|
15
16
|
import { callRpc, rpcFailure, rpcSuccess, unwrapRpcResult } from "./result.js";
|
|
16
|
-
import { parseBrowserAction, parseBrowserActionOutcomeResult, parseBootstrapResult, parseHostReconfiguration, parseContext, parseErrorData, parseFact, parseAgentSessionSelection, parseIdentity, parseLifecycle, parseNavigation, parseStatusAction, parseStatusItems, parseNavigationStateInput, parsePermissionOutcomeResult, parsePermissionRequest, parseReservedChords, parseTheme, parseToast, parseVoidResult, } from "./schemas.js";
|
|
17
|
+
import { parseBrowserAction, parseBrowserActionOutcomeResult, parseBootstrapResult, parseHostReconfiguration, parseContext, parseErrorData, parseFact, parseAgentSessionSelection, parseIdentity, parseLifecycle, parseNavigation, parseNavigationActivation, parseNavigationState, parseStatusAction, parseStatusItems, parseNavigationStateInput, parsePermissionOutcomeResult, parsePermissionRequest, parseReservedChords, parseTheme, parseToast, parseVoidResult, } from "./schemas.js";
|
|
17
18
|
const rpcStubSchema = Schema.declare((value) => value instanceof RpcStub);
|
|
18
19
|
import { createReadonlyStore } from "./store.js";
|
|
19
20
|
class AppClientTarget extends RpcTarget {
|
|
@@ -57,6 +58,21 @@ class AppClientTarget extends RpcTarget {
|
|
|
57
58
|
return rpcFailure(cause);
|
|
58
59
|
}
|
|
59
60
|
}
|
|
61
|
+
async activateNavigation(value) {
|
|
62
|
+
try {
|
|
63
|
+
if (this.#disposed)
|
|
64
|
+
throw disconnected();
|
|
65
|
+
const activation = parseNavigationActivation(value);
|
|
66
|
+
// A shell's stale copy can still send a crumb or command the App has
|
|
67
|
+
// since dropped; only what it published now reaches the App.
|
|
68
|
+
if (!navigationActivationAllowed(this.#state.navigation, activation))
|
|
69
|
+
return rpcSuccess(false);
|
|
70
|
+
return rpcSuccess(await (this.#state.activate?.(activation) ?? false));
|
|
71
|
+
}
|
|
72
|
+
catch (cause) {
|
|
73
|
+
return rpcFailure(cause);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
60
76
|
async activateStatus(value) {
|
|
61
77
|
try {
|
|
62
78
|
if (this.#disposed)
|
|
@@ -231,6 +247,7 @@ function createBrokenPromise(remote) {
|
|
|
231
247
|
function createClientState() {
|
|
232
248
|
return {
|
|
233
249
|
statusItems: [],
|
|
250
|
+
navigation: { canGoBack: false },
|
|
234
251
|
bindings: createLocalBindingState(),
|
|
235
252
|
chords: createReservedChordState([]),
|
|
236
253
|
context: createReadonlyStore({
|
|
@@ -293,6 +310,7 @@ function createRootRuntime(options, identity, capabilities) {
|
|
|
293
310
|
() => {
|
|
294
311
|
options.state.beforeClose = undefined;
|
|
295
312
|
options.state.back = undefined;
|
|
313
|
+
options.state.activate = undefined;
|
|
296
314
|
options.state.statusAction = undefined;
|
|
297
315
|
},
|
|
298
316
|
() => {
|
|
@@ -370,8 +388,26 @@ function createAppWindow(state, remote, assertConnected) {
|
|
|
370
388
|
assertConnected();
|
|
371
389
|
const wire = parseNavigationStateInput(input);
|
|
372
390
|
state.canGoBack = wire.canGoBack;
|
|
391
|
+
state.navigation = parseNavigationState(wire);
|
|
373
392
|
await callRpc(remote.reportNavigation(wire), parseVoidResult);
|
|
374
393
|
},
|
|
394
|
+
onActivate(handler) {
|
|
395
|
+
assertConnected();
|
|
396
|
+
if (state.activate !== undefined)
|
|
397
|
+
throw new AppError("The App already owns navigation activations", {
|
|
398
|
+
code: "INVALID_ARGUMENT",
|
|
399
|
+
retryable: false,
|
|
400
|
+
});
|
|
401
|
+
state.activate = handler;
|
|
402
|
+
let released = false;
|
|
403
|
+
return () => {
|
|
404
|
+
if (released)
|
|
405
|
+
return;
|
|
406
|
+
released = true;
|
|
407
|
+
if (state.activate === handler)
|
|
408
|
+
state.activate = undefined;
|
|
409
|
+
};
|
|
410
|
+
},
|
|
375
411
|
onBack(handler) {
|
|
376
412
|
assertConnected();
|
|
377
413
|
if (state.back !== undefined)
|
|
@@ -2,7 +2,7 @@ import type { AppCommand } from "../commands.js";
|
|
|
2
2
|
import type { WhistleExecutionOrigin, WhistleJsonValue } from "@fungi.computer/whistle";
|
|
3
3
|
import type { AppContext, AppIdentity, AppLifecycle, FocusState } from "../lifecycle.js";
|
|
4
4
|
import type { AppBrowserAction, AppBrowserActionOutcome, AgentSessionSelection, AppPermissionOutcome, AppPermissionRequest, AppStatusAction, AppStatusItem, FileNavigationInput, ToastInput } from "../ui.js";
|
|
5
|
-
import type { APP_NAVIGATION_CONTRACT_VERSION } from "../window.js";
|
|
5
|
+
import type { APP_NAVIGATION_CONTRACT_VERSION, AppNavigationActivation, AppNavigationState } from "../window.js";
|
|
6
6
|
import type { AppErrorData } from "./app-error.js";
|
|
7
7
|
import type { RpcResult } from "./result.js";
|
|
8
8
|
import type { WhistleFailure } from "@fungi.computer/whistle";
|
|
@@ -17,6 +17,8 @@ export type AppClientRpc = {
|
|
|
17
17
|
requestClose(): Promise<RpcResult<boolean>>;
|
|
18
18
|
/** Ask the App to go back one step in its own history; `true` when it did. */
|
|
19
19
|
requestBack(): Promise<RpcResult<boolean>>;
|
|
20
|
+
/** The person used a crumb or header command; `true` when the App acted. */
|
|
21
|
+
activateNavigation(activation: AppNavigationActivation): Promise<RpcResult<boolean>>;
|
|
20
22
|
/** The person used one of the App's status items; `true` when it acted. */
|
|
21
23
|
activateStatus(action: AppStatusAction): Promise<RpcResult<boolean>>;
|
|
22
24
|
updateContext(context: VersionedFact<AppContext>): Promise<RpcResult<void>>;
|
|
@@ -51,9 +53,7 @@ export type AppHostRpc = {
|
|
|
51
53
|
/** The versioned navigation contract an App reports to its host. */
|
|
52
54
|
export type NavigationStateWire = Readonly<{
|
|
53
55
|
contractVersion: typeof APP_NAVIGATION_CONTRACT_VERSION;
|
|
54
|
-
|
|
55
|
-
title?: string;
|
|
56
|
-
}>;
|
|
56
|
+
}> & AppNavigationState;
|
|
57
57
|
/** The App iframe's handshake message announcing it is ready to connect. */
|
|
58
58
|
export type AppReadyMessage = {
|
|
59
59
|
readonly protocolVersion: typeof APP_PROTOCOL_VERSION;
|
|
@@ -8,9 +8,9 @@ import type { WhistleJsonValue } from "@fungi.computer/whistle";
|
|
|
8
8
|
import type { AppContext, AppIdentity, AppLifecycle, FocusState } from "../lifecycle.js";
|
|
9
9
|
import type { AppBrowserAction, AppBrowserActionRefusal, AppBrowserActionOutcome, AppPermissionOutcome, AppStatusItem, FileNavigationInput, ToastInput } from "../ui.js";
|
|
10
10
|
import type { AppErrorData } from "./app-error.js";
|
|
11
|
-
import type { CapabilityGrantWire, AppHostReconfigurationWire, CommandInvocationResultWire, HostBootstrapWire, RegisteredCommandWire } from "./protocol.js";
|
|
11
|
+
import type { CapabilityGrantWire, AppHostReconfigurationWire, CommandInvocationResultWire, HostBootstrapWire, NavigationStateWire, RegisteredCommandWire } from "./protocol.js";
|
|
12
12
|
import type { RpcResult } from "./result.js";
|
|
13
|
-
import {
|
|
13
|
+
import { type AppNavigationActivation } from "../window.js";
|
|
14
14
|
import { type AppConnectMessage, type AppReadyMessage, type CommandInvocationWire, type ConnectRequestWire, type ReservedChord, type VersionedFact } from "./protocol.js";
|
|
15
15
|
export declare function appInvocation(value: CommandInvocationWire, signal: AbortSignal): Readonly<AppCommandInvocation>;
|
|
16
16
|
export declare const parseCommand: (input: unknown, options?: import("effect/SchemaAST").ParseOptions | undefined) => Readonly<{
|
|
@@ -38,6 +38,7 @@ export declare const parseCommand: (input: unknown, options?: import("effect/Sch
|
|
|
38
38
|
} | undefined;
|
|
39
39
|
readonly slash?: {
|
|
40
40
|
readonly aliases: readonly string[];
|
|
41
|
+
readonly consumesDraft?: boolean | undefined;
|
|
41
42
|
readonly order?: number | undefined;
|
|
42
43
|
readonly arguments?: {
|
|
43
44
|
readonly hint: string;
|
|
@@ -263,13 +264,38 @@ export declare const parseNavigation: (input: unknown, options?: import("effect/
|
|
|
263
264
|
export declare const parseNavigationState: (input: unknown, options?: import("effect/SchemaAST").ParseOptions | undefined) => Readonly<{
|
|
264
265
|
canGoBack: boolean;
|
|
265
266
|
title?: string;
|
|
267
|
+
crumbs?: readonly Readonly<{
|
|
268
|
+
id: string;
|
|
269
|
+
label: string;
|
|
270
|
+
siblings?: readonly Readonly<{
|
|
271
|
+
id: string;
|
|
272
|
+
label: string;
|
|
273
|
+
}>[];
|
|
274
|
+
}>[];
|
|
275
|
+
primary?: readonly Readonly<{
|
|
276
|
+
id: string;
|
|
277
|
+
label: string;
|
|
278
|
+
variant?: "primary" | "outline" | "ghost";
|
|
279
|
+
tone?: "destructive";
|
|
280
|
+
disabled?: boolean;
|
|
281
|
+
}>[];
|
|
282
|
+
menu?: readonly Readonly<{
|
|
283
|
+
id: string;
|
|
284
|
+
label: string;
|
|
285
|
+
variant?: "primary" | "outline" | "ghost";
|
|
286
|
+
tone?: "destructive";
|
|
287
|
+
disabled?: boolean;
|
|
288
|
+
}>[];
|
|
289
|
+
status?: readonly Readonly<{
|
|
290
|
+
id: string;
|
|
291
|
+
label: string;
|
|
292
|
+
tone?: "neutral" | "info" | "warning" | "success" | "error";
|
|
293
|
+
}>[];
|
|
266
294
|
}>;
|
|
267
295
|
/** App edge: trim the title, drop an empty one, then check the wire bounds. */
|
|
268
|
-
export declare const parseNavigationStateInput: (input: unknown, options?: import("effect/SchemaAST").ParseOptions | undefined) =>
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
title?: string;
|
|
272
|
-
}>;
|
|
296
|
+
export declare const parseNavigationStateInput: (input: unknown, options?: import("effect/SchemaAST").ParseOptions | undefined) => NavigationStateWire;
|
|
297
|
+
/** Host to App: one crumb or command the person used. */
|
|
298
|
+
export declare const parseNavigationActivation: (input: unknown, options?: import("effect/SchemaAST").ParseOptions | undefined) => AppNavigationActivation;
|
|
273
299
|
export declare const parseAgentSessionSelection: (input: unknown, options?: import("effect/SchemaAST").ParseOptions | undefined) => Readonly<{
|
|
274
300
|
agentId: string;
|
|
275
301
|
sessionId: string;
|
package/dist/internal/schemas.js
CHANGED
|
@@ -9,7 +9,7 @@ import { flow } from "effect/Function";
|
|
|
9
9
|
import { parseCommandDefinition, parseJsonValue, } from "@fungi.computer/whistle/wire";
|
|
10
10
|
import { isUnsafeStatusCodeUnit } from "../status.js";
|
|
11
11
|
import { APP_BROWSER_SAVE_MAX_BYTES, APP_BROWSER_COPY_MAX_BYTES, APP_STATUS_ITEMS_MAX, APP_STATUS_OPTIONS_MAX, APP_STATUS_TEXT_MAX, } from "../ui.js";
|
|
12
|
-
import { APP_NAVIGATION_CONTRACT_VERSION, APP_NAVIGATION_TITLE_MAX, } from "../window.js";
|
|
12
|
+
import { APP_NAVIGATION_CONTRACT_VERSION, APP_NAVIGATION_CRUMBS_MAX, APP_NAVIGATION_LABEL_MAX, APP_NAVIGATION_MENU_MAX, APP_NAVIGATION_PRIMARY_MAX, APP_NAVIGATION_SIBLINGS_MAX, APP_NAVIGATION_STATUS_MAX, APP_NAVIGATION_TITLE_MAX, } from "../window.js";
|
|
13
13
|
import { AppError } from "../errors.js";
|
|
14
14
|
import { parseFailure } from "@fungi.computer/whistle/wire";
|
|
15
15
|
import { APP_CONNECT_TYPE, APP_PROTOCOL_VERSION, APP_READY_TYPE, } from "./protocol.js";
|
|
@@ -175,15 +175,62 @@ const navigationSchema = Schema.Struct({
|
|
|
175
175
|
const navigationTitleSchema = boundedText(APP_NAVIGATION_TITLE_MAX).pipe(Schema.check(Schema.makeFilter((title) => title.trim() === title, {
|
|
176
176
|
message: "Expected a trimmed title",
|
|
177
177
|
})));
|
|
178
|
+
const navigationIdSchema = boundedText(64);
|
|
179
|
+
const navigationLabelSchema = boundedText(APP_NAVIGATION_LABEL_MAX);
|
|
180
|
+
/** Ids name what a person used; repeats would make an activation ambiguous. */
|
|
181
|
+
const uniqueIds = (items) => new Set(items.map((item) => item.id)).size === items.length;
|
|
182
|
+
const crumbSchema = Schema.Struct({
|
|
183
|
+
id: navigationIdSchema,
|
|
184
|
+
label: navigationLabelSchema,
|
|
185
|
+
siblings: Schema.optional(Schema.Array(Schema.Struct({ id: navigationIdSchema, label: navigationLabelSchema })).pipe(Schema.check(Schema.isMaxLength(APP_NAVIGATION_SIBLINGS_MAX)), Schema.check(Schema.makeFilter(uniqueIds, {
|
|
186
|
+
message: "Expected unique crumb sibling ids",
|
|
187
|
+
})))),
|
|
188
|
+
});
|
|
189
|
+
const headerCommandSchema = Schema.Struct({
|
|
190
|
+
id: navigationIdSchema,
|
|
191
|
+
label: navigationLabelSchema,
|
|
192
|
+
variant: Schema.optional(Schema.Literals(["primary", "outline", "ghost"])),
|
|
193
|
+
tone: Schema.optional(Schema.Literal("destructive")),
|
|
194
|
+
disabled: Schema.optional(Schema.Boolean),
|
|
195
|
+
});
|
|
196
|
+
const commandsSchema = (maximum) => Schema.Array(headerCommandSchema).pipe(Schema.check(Schema.isMaxLength(maximum)), Schema.check(Schema.makeFilter(uniqueIds, {
|
|
197
|
+
message: "Expected unique header command ids",
|
|
198
|
+
})));
|
|
199
|
+
const headerStatusSchema = Schema.Struct({
|
|
200
|
+
id: navigationIdSchema,
|
|
201
|
+
label: navigationLabelSchema,
|
|
202
|
+
tone: Schema.optional(Schema.Literals(["neutral", "info", "warning", "success", "error"])),
|
|
203
|
+
});
|
|
204
|
+
const navigationFields = {
|
|
205
|
+
status: Schema.optional(Schema.Array(headerStatusSchema).pipe(Schema.check(Schema.isMaxLength(APP_NAVIGATION_STATUS_MAX)), Schema.check(Schema.makeFilter(uniqueIds, {
|
|
206
|
+
message: "Expected unique header status ids",
|
|
207
|
+
})))),
|
|
208
|
+
crumbs: Schema.optional(Schema.Array(crumbSchema).pipe(Schema.check(Schema.isMaxLength(APP_NAVIGATION_CRUMBS_MAX)), Schema.check(Schema.makeFilter(uniqueIds, { message: "Expected unique crumb ids" })))),
|
|
209
|
+
primary: Schema.optional(commandsSchema(APP_NAVIGATION_PRIMARY_MAX)),
|
|
210
|
+
menu: Schema.optional(commandsSchema(APP_NAVIGATION_MENU_MAX)),
|
|
211
|
+
};
|
|
178
212
|
const navigationStateWireSchema = Schema.Struct({
|
|
179
213
|
contractVersion: Schema.Literal(APP_NAVIGATION_CONTRACT_VERSION),
|
|
180
214
|
canGoBack: Schema.Boolean,
|
|
181
215
|
title: Schema.optional(navigationTitleSchema),
|
|
216
|
+
...navigationFields,
|
|
182
217
|
});
|
|
183
218
|
const navigationStateInputSchema = Schema.Struct({
|
|
184
219
|
canGoBack: Schema.Boolean,
|
|
185
220
|
title: Schema.optional(Schema.String),
|
|
221
|
+
...navigationFields,
|
|
186
222
|
});
|
|
223
|
+
const navigationActivationSchema = Schema.Union([
|
|
224
|
+
Schema.Struct({
|
|
225
|
+
kind: Schema.Literal("crumb"),
|
|
226
|
+
crumbId: navigationIdSchema,
|
|
227
|
+
siblingId: Schema.optional(navigationIdSchema),
|
|
228
|
+
}),
|
|
229
|
+
Schema.Struct({
|
|
230
|
+
kind: Schema.Literal("action"),
|
|
231
|
+
actionId: navigationIdSchema,
|
|
232
|
+
}),
|
|
233
|
+
]);
|
|
187
234
|
const agentSessionSelectionSchema = Schema.NullOr(Schema.Struct({
|
|
188
235
|
agentId: boundedText(256),
|
|
189
236
|
sessionId: boundedText(256),
|
|
@@ -339,25 +386,69 @@ export const parseStatusAction = decoder(Schema.Struct({
|
|
|
339
386
|
? { ...action }
|
|
340
387
|
: { ...action, anchor: frozen({ ...action.anchor }) }), "INVALID_ARGUMENT");
|
|
341
388
|
export const parseNavigation = decoder(navigationSchema, (value) => frozen({ ...value }), "INVALID_ARGUMENT");
|
|
389
|
+
function statusOf(item) {
|
|
390
|
+
const copy = { id: item.id, label: item.label };
|
|
391
|
+
return frozen(item.tone === undefined ? copy : { ...copy, tone: item.tone });
|
|
392
|
+
}
|
|
393
|
+
function crumbOf(crumb) {
|
|
394
|
+
const copy = { id: crumb.id, label: crumb.label };
|
|
395
|
+
if (crumb.siblings === undefined)
|
|
396
|
+
return frozen(copy);
|
|
397
|
+
return frozen({
|
|
398
|
+
...copy,
|
|
399
|
+
siblings: frozen(crumb.siblings.map((item) => frozen({ ...item }))),
|
|
400
|
+
});
|
|
401
|
+
}
|
|
402
|
+
function commandOf(command) {
|
|
403
|
+
let copy = { id: command.id, label: command.label };
|
|
404
|
+
if (command.variant !== undefined)
|
|
405
|
+
copy = { ...copy, variant: command.variant };
|
|
406
|
+
if (command.tone !== undefined)
|
|
407
|
+
copy = { ...copy, tone: command.tone };
|
|
408
|
+
if (command.disabled !== undefined)
|
|
409
|
+
copy = { ...copy, disabled: command.disabled };
|
|
410
|
+
return frozen(copy);
|
|
411
|
+
}
|
|
412
|
+
/** One report's fields, each present only when the App sent it. */
|
|
413
|
+
function navigationOf(value) {
|
|
414
|
+
let state = { canGoBack: value.canGoBack };
|
|
415
|
+
if (value.title !== undefined)
|
|
416
|
+
state = { ...state, title: value.title };
|
|
417
|
+
if (value.crumbs !== undefined)
|
|
418
|
+
state = { ...state, crumbs: frozen(value.crumbs.map(crumbOf)) };
|
|
419
|
+
if (value.primary !== undefined)
|
|
420
|
+
state = { ...state, primary: frozen(value.primary.map(commandOf)) };
|
|
421
|
+
if (value.menu !== undefined)
|
|
422
|
+
state = { ...state, menu: frozen(value.menu.map(commandOf)) };
|
|
423
|
+
if (value.status !== undefined)
|
|
424
|
+
state = { ...state, status: frozen(value.status.map(statusOf)) };
|
|
425
|
+
return frozen(state);
|
|
426
|
+
}
|
|
342
427
|
/** Host edge: one App report, projected without its contract version. */
|
|
343
|
-
export const parseNavigationState = decoder(navigationStateWireSchema,
|
|
344
|
-
? { canGoBack: value.canGoBack }
|
|
345
|
-
: { canGoBack: value.canGoBack, title: value.title }), "INVALID_ARGUMENT");
|
|
428
|
+
export const parseNavigationState = decoder(navigationStateWireSchema, navigationOf, "INVALID_ARGUMENT");
|
|
346
429
|
/** App edge: trim the title, drop an empty one, then check the wire bounds. */
|
|
347
430
|
export const parseNavigationStateInput = decoder(navigationStateInputSchema, (value) => {
|
|
348
|
-
const
|
|
349
|
-
return parseNavigationStateWire(
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
}
|
|
354
|
-
:
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
431
|
+
const trimmed = value.title?.trim();
|
|
432
|
+
return parseNavigationStateWire({
|
|
433
|
+
...navigationOf({
|
|
434
|
+
...value,
|
|
435
|
+
title: trimmed === undefined || trimmed.length === 0 ? undefined : trimmed,
|
|
436
|
+
}),
|
|
437
|
+
contractVersion: APP_NAVIGATION_CONTRACT_VERSION,
|
|
438
|
+
});
|
|
439
|
+
}, "INVALID_ARGUMENT");
|
|
440
|
+
const parseNavigationStateWire = decoder(navigationStateWireSchema, (value) => frozen({
|
|
441
|
+
...navigationOf(value),
|
|
442
|
+
contractVersion: APP_NAVIGATION_CONTRACT_VERSION,
|
|
443
|
+
}), "INVALID_ARGUMENT");
|
|
444
|
+
/** Host to App: one crumb or command the person used. */
|
|
445
|
+
export const parseNavigationActivation = decoder(navigationActivationSchema, (value) => {
|
|
446
|
+
if (value.kind === "action")
|
|
447
|
+
return frozen({ ...value });
|
|
448
|
+
return frozen(value.siblingId === undefined
|
|
449
|
+
? { kind: value.kind, crumbId: value.crumbId }
|
|
450
|
+
: value);
|
|
359
451
|
}, "INVALID_ARGUMENT");
|
|
360
|
-
const parseNavigationStateWire = decoder(navigationStateWireSchema, (value) => frozen({ ...value }), "INVALID_ARGUMENT");
|
|
361
452
|
export const parseAgentSessionSelection = decoder(agentSessionSelectionSchema, (value) => value === null ? null : frozen({ ...value }), "INVALID_ARGUMENT");
|
|
362
453
|
export const parsePermissionRequest = decoder(permissionRequestSchema, (value) => frozen({
|
|
363
454
|
agents: frozen([...new Set(value.agents ?? [])]),
|
package/dist/marketplace.js
CHANGED
|
@@ -72,19 +72,21 @@ export const marketplaceAcquisitionSchema = Schema.Union([
|
|
|
72
72
|
}),
|
|
73
73
|
]);
|
|
74
74
|
/** Read the Marketplace gallery as the Team that mounted this App. */
|
|
75
|
-
export const marketplaceBrowse = defineCapability("marketplace.browse", 1, rpcHandleSchema({
|
|
76
|
-
list: rpcMethod(rpcDataSchema(z
|
|
77
|
-
|
|
75
|
+
export const marketplaceBrowse = defineCapability("marketplace.browse", 1, z.lazy(() => rpcHandleSchema({
|
|
76
|
+
list: rpcMethod(rpcDataSchema(z
|
|
77
|
+
.unknown()
|
|
78
|
+
.transform((value) => decodeMarketplaceBrowseResult(value)))),
|
|
79
|
+
})));
|
|
78
80
|
/**
|
|
79
81
|
* Acquire a listing for the mounting Team. Free listings install at once;
|
|
80
82
|
* paid listings return a hosted Checkout URL and install after payment.
|
|
81
83
|
* The host re-checks Team authority and entitlement on every call.
|
|
82
84
|
*/
|
|
83
|
-
export const marketplacePurchase = defineCapability("marketplace.purchase", 1, rpcHandleSchema({
|
|
85
|
+
export const marketplacePurchase = defineCapability("marketplace.purchase", 1, z.lazy(() => rpcHandleSchema({
|
|
84
86
|
acquire: rpcMethod(rpcDataSchema(z
|
|
85
87
|
.unknown()
|
|
86
88
|
.transform((value) => Schema.decodeUnknownSync(marketplaceAcquisitionSchema)(value)))),
|
|
87
|
-
}));
|
|
89
|
+
})));
|
|
88
90
|
/** Anonymous discovery only; acquisition and launch still require Team admission. */
|
|
89
91
|
export async function browsePublicMarketplace(hubOrigin, category, request = fetch) {
|
|
90
92
|
const url = new URL("/api/public/marketplace/listings", hubOrigin);
|
|
@@ -4,4 +4,4 @@ export declare const mediaRequestIds: Readonly<{
|
|
|
4
4
|
readonly request: import("@fungi.computer/cairn/id").IdCodec<"hub-media-request", "request", 1>;
|
|
5
5
|
}>;
|
|
6
6
|
/** Public wire grammar for the canonical retry identity, visible in App JSON schemas. */
|
|
7
|
-
export declare const mediaRequestIdSchema: z.ZodString
|
|
7
|
+
export declare const mediaRequestIdSchema: z.ZodLazy<z.ZodString>;
|
package/dist/media-request-id.js
CHANGED
|
@@ -10,7 +10,7 @@ export const mediaRequestIds = defineIdFamily({
|
|
|
10
10
|
},
|
|
11
11
|
});
|
|
12
12
|
/** Public wire grammar for the canonical retry identity, visible in App JSON schemas. */
|
|
13
|
-
export const mediaRequestIdSchema = z
|
|
13
|
+
export const mediaRequestIdSchema = z.lazy(() => z
|
|
14
14
|
.string()
|
|
15
15
|
.length(35)
|
|
16
|
-
.regex(/^media_request_[a-z0-9]{21}$/);
|
|
16
|
+
.regex(/^media_request_[a-z0-9]{21}$/));
|
package/dist/media.js
CHANGED
|
@@ -327,7 +327,7 @@ const mediaAttemptHandleSchema = rpcHandleSchema({
|
|
|
327
327
|
/** Capability contract for generating Media (images or video). Apps
|
|
328
328
|
* declare it to request access; openMediaGeneration uses it to obtain
|
|
329
329
|
* the host's handle. */
|
|
330
|
-
export const mediaGeneration = defineCapability("media.generation", 5, rpcHandleSchema({
|
|
330
|
+
export const mediaGeneration = defineCapability("media.generation", 5, z.lazy(() => rpcHandleSchema({
|
|
331
331
|
describe: rpcMethod(rpcDataSchema(z.unknown().transform((value) => {
|
|
332
332
|
const { computerId, canGenerate, models } = Schema.decodeUnknownSync(descriptionSchema, {
|
|
333
333
|
onExcessProperty: "error",
|
|
@@ -337,7 +337,7 @@ export const mediaGeneration = defineCapability("media.generation", 5, rpcHandle
|
|
|
337
337
|
createAttempt: rpcMethod(mediaAttemptHandleSchema),
|
|
338
338
|
restoreAttempt: rpcMethod(mediaAttemptHandleSchema),
|
|
339
339
|
cancelAttempt: rpcVoid,
|
|
340
|
-
}));
|
|
340
|
+
})));
|
|
341
341
|
/** Parse a value received from the host and turn schema failures into the
|
|
342
342
|
* transport-safe Agar error used by the other capability helpers. */
|
|
343
343
|
function decode(schema, value) {
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { ResultRowCodec } from "./backend-definition.js";
|
|
2
|
+
/**
|
|
3
|
+
* The shared result row's decoder and JSON boundary, for
|
|
4
|
+
* `defineAppMethod({ present: { verb, row, rows: resultRows } })`. Only
|
|
5
|
+
* backends that present import this module, so the rest carry no decoder.
|
|
6
|
+
*/
|
|
7
|
+
export declare const resultRows: ResultRowCodec;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import * as z from "zod";
|
|
2
|
+
import * as Schema from "effect/Schema";
|
|
3
|
+
import { resultRowSchemaV1 } from "@fungi.computer/gills";
|
|
4
|
+
/**
|
|
5
|
+
* The shared result row's decoder and JSON boundary, for
|
|
6
|
+
* `defineAppMethod({ present: { verb, row, rows: resultRows } })`. Only
|
|
7
|
+
* backends that present import this module, so the rest carry no decoder.
|
|
8
|
+
*/
|
|
9
|
+
export const resultRows = Object.freeze({
|
|
10
|
+
decode: Schema.decodeUnknownSync(resultRowSchemaV1, {
|
|
11
|
+
onExcessProperty: "error",
|
|
12
|
+
}),
|
|
13
|
+
schema: z.fromJSONSchema(Schema.toJsonSchemaDocument(resultRowSchemaV1).schema),
|
|
14
|
+
});
|
package/dist/method-refusal.d.ts
CHANGED
|
@@ -29,7 +29,7 @@ export declare class AppMethodRefusal extends Error {
|
|
|
29
29
|
constructor(code: AppMethodRefusalCode, message: string);
|
|
30
30
|
}
|
|
31
31
|
/** A refused call as it crosses the generated method boundary. */
|
|
32
|
-
export declare const appMethodRefusedReplySchema: z.ZodObject<{
|
|
32
|
+
export declare const appMethodRefusedReplySchema: z.ZodLazy<z.ZodObject<{
|
|
33
33
|
kind: z.ZodLiteral<"refused">;
|
|
34
34
|
code: z.ZodEnum<{
|
|
35
35
|
unavailable: "unavailable";
|
|
@@ -39,14 +39,14 @@ export declare const appMethodRefusedReplySchema: z.ZodObject<{
|
|
|
39
39
|
not_found: "not_found";
|
|
40
40
|
}>;
|
|
41
41
|
message: z.ZodString;
|
|
42
|
-
}, z.core.$strict
|
|
42
|
+
}, z.core.$strict>>;
|
|
43
43
|
/** A decoded {@link appMethodRefusedReplySchema} value. */
|
|
44
44
|
export type AppMethodRefusedReply = z.output<typeof appMethodRefusedReplySchema>;
|
|
45
45
|
/** Every generated App method RPC answers with exactly one of these. */
|
|
46
|
-
export declare const appMethodReplySchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
46
|
+
export declare const appMethodReplySchema: z.ZodLazy<z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
47
47
|
kind: z.ZodLiteral<"ok">;
|
|
48
48
|
value: z.ZodJSONSchema;
|
|
49
|
-
}, z.core.$strict>, z.ZodObject<{
|
|
49
|
+
}, z.core.$strict>, z.ZodLazy<z.ZodObject<{
|
|
50
50
|
kind: z.ZodLiteral<"refused">;
|
|
51
51
|
code: z.ZodEnum<{
|
|
52
52
|
unavailable: "unavailable";
|
|
@@ -56,7 +56,7 @@ export declare const appMethodReplySchema: z.ZodDiscriminatedUnion<[z.ZodObject<
|
|
|
56
56
|
not_found: "not_found";
|
|
57
57
|
}>;
|
|
58
58
|
message: z.ZodString;
|
|
59
|
-
}, z.core.$strict
|
|
59
|
+
}, z.core.$strict>>], "kind">>;
|
|
60
60
|
/** A decoded {@link appMethodReplySchema} value; the result is read-only JSON. */
|
|
61
61
|
export type AppMethodReply = Readonly<{
|
|
62
62
|
kind: "ok";
|
package/dist/method-refusal.js
CHANGED
|
@@ -36,13 +36,13 @@ export class AppMethodRefusal extends Error {
|
|
|
36
36
|
}
|
|
37
37
|
}
|
|
38
38
|
/** A refused call as it crosses the generated method boundary. */
|
|
39
|
-
export const appMethodRefusedReplySchema = z.strictObject({
|
|
39
|
+
export const appMethodRefusedReplySchema = z.lazy(() => z.strictObject({
|
|
40
40
|
kind: z.literal("refused"),
|
|
41
41
|
code: z.enum(appMethodRefusalCodes),
|
|
42
42
|
message: z.string().min(1).max(maxAppMethodRefusalMessage),
|
|
43
|
-
});
|
|
43
|
+
}));
|
|
44
44
|
/** Every generated App method RPC answers with exactly one of these. */
|
|
45
|
-
export const appMethodReplySchema = z.discriminatedUnion("kind", [
|
|
45
|
+
export const appMethodReplySchema = z.lazy(() => z.discriminatedUnion("kind", [
|
|
46
46
|
z.strictObject({ kind: z.literal("ok"), value: z.json() }),
|
|
47
47
|
appMethodRefusedReplySchema,
|
|
48
|
-
]);
|
|
48
|
+
]));
|
package/dist/react.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type WhistleContribution, type WhistleRuntime } from "@fungi.computer/whistle";
|
|
2
|
-
import type { AppBackHandler, HostNavigation } from "./window.js";
|
|
2
|
+
import type { AppBackHandler, AppCrumb, AppHeaderCommand, AppHeaderStatus, HostNavigation } from "./window.js";
|
|
3
3
|
import type { AppContext } from "./lifecycle.js";
|
|
4
4
|
import type { AppStatusActionHandler, AppStatusItem } from "./ui.js";
|
|
5
5
|
import type { AppRoot } from "./root.js";
|
|
@@ -31,11 +31,23 @@ export type HostNavigationOptions = Readonly<{
|
|
|
31
31
|
canGoBack: boolean;
|
|
32
32
|
/** The current page's title, shown after the App name in window chrome. */
|
|
33
33
|
title?: string | undefined;
|
|
34
|
+
/** The App's path, shown in the window header in place of the title. */
|
|
35
|
+
crumbs?: readonly AppCrumb[] | undefined;
|
|
36
|
+
/** The view's main commands, as header buttons. */
|
|
37
|
+
primary?: readonly AppHeaderCommand[] | undefined;
|
|
38
|
+
/** Less common commands, behind the header's overflow menu. */
|
|
39
|
+
menu?: readonly AppHeaderCommand[] | undefined;
|
|
40
|
+
/** Facts about the view, shown as muted text with a status bead. */
|
|
41
|
+
status?: readonly AppHeaderStatus[] | undefined;
|
|
34
42
|
onBack: AppBackHandler;
|
|
43
|
+
/** The person chose a crumb, or a sibling from a switcher crumb. */
|
|
44
|
+
onCrumb?: ((crumbId: string, siblingId?: string) => void) | undefined;
|
|
45
|
+
/** The person used a primary or menu command. */
|
|
46
|
+
onAction?: ((actionId: string) => void) | undefined;
|
|
35
47
|
}>;
|
|
36
48
|
/**
|
|
37
49
|
* Publish an App's navigation state to its host and own the host's Back
|
|
38
50
|
* requests while mounted. Pass `app?.window.navigation`; with no connected
|
|
39
51
|
* App it does nothing. Unmounting withdraws Back from the host chrome.
|
|
40
52
|
*/
|
|
41
|
-
export declare function useHostNavigation(navigation: HostNavigation | null | undefined, { canGoBack, title, onBack }: HostNavigationOptions): void;
|
|
53
|
+
export declare function useHostNavigation(navigation: HostNavigation | null | undefined, { canGoBack, title, crumbs, primary, menu, status, onBack, onCrumb, onAction, }: HostNavigationOptions): void;
|
package/dist/react.js
CHANGED
|
@@ -80,26 +80,62 @@ export function useAppCommandCatalog(app, { sourceId, namespace }, build) {
|
|
|
80
80
|
* requests while mounted. Pass `app?.window.navigation`; with no connected
|
|
81
81
|
* App it does nothing. Unmounting withdraws Back from the host chrome.
|
|
82
82
|
*/
|
|
83
|
-
export function useHostNavigation(navigation, { canGoBack, title, onBack }) {
|
|
83
|
+
export function useHostNavigation(navigation, { canGoBack, title, crumbs, primary, menu, status, onBack, onCrumb, onAction, }) {
|
|
84
84
|
const latestBack = useRef(onBack);
|
|
85
|
+
const latestCrumb = useRef(onCrumb);
|
|
86
|
+
const latestAction = useRef(onAction);
|
|
85
87
|
useEffect(() => {
|
|
86
88
|
latestBack.current = onBack;
|
|
87
|
-
|
|
89
|
+
latestCrumb.current = onCrumb;
|
|
90
|
+
latestAction.current = onAction;
|
|
91
|
+
}, [onBack, onCrumb, onAction]);
|
|
88
92
|
useEffect(() => {
|
|
89
93
|
if (!navigation)
|
|
90
94
|
return undefined;
|
|
91
95
|
const release = navigation.onBack(() => latestBack.current());
|
|
96
|
+
const releaseActivation = navigation.onActivate((activation) => {
|
|
97
|
+
if (activation.kind === "action") {
|
|
98
|
+
const handler = latestAction.current;
|
|
99
|
+
handler?.(activation.actionId);
|
|
100
|
+
return handler !== undefined;
|
|
101
|
+
}
|
|
102
|
+
const handler = latestCrumb.current;
|
|
103
|
+
if (activation.siblingId === undefined)
|
|
104
|
+
handler?.(activation.crumbId);
|
|
105
|
+
else
|
|
106
|
+
handler?.(activation.crumbId, activation.siblingId);
|
|
107
|
+
return handler !== undefined;
|
|
108
|
+
});
|
|
92
109
|
return () => {
|
|
93
110
|
release();
|
|
111
|
+
releaseActivation();
|
|
94
112
|
report(navigation, { canGoBack: false });
|
|
95
113
|
};
|
|
96
114
|
}, [navigation]);
|
|
115
|
+
// Callers pass fresh arrays each render; the report follows their content.
|
|
116
|
+
const lists = JSON.stringify([crumbs, primary, menu, status]);
|
|
117
|
+
const latestLists = useRef({ crumbs, primary, menu, status });
|
|
118
|
+
useEffect(() => {
|
|
119
|
+
latestLists.current = { crumbs, primary, menu, status };
|
|
120
|
+
}, [crumbs, primary, menu, status]);
|
|
97
121
|
// Each change replaces the last report; only unmount withdraws Back.
|
|
98
122
|
useEffect(() => {
|
|
99
123
|
if (!navigation)
|
|
100
124
|
return;
|
|
101
|
-
|
|
102
|
-
|
|
125
|
+
let state = { canGoBack };
|
|
126
|
+
if (title !== undefined)
|
|
127
|
+
state = { ...state, title };
|
|
128
|
+
const current = latestLists.current;
|
|
129
|
+
if (current.crumbs !== undefined)
|
|
130
|
+
state = { ...state, crumbs: current.crumbs };
|
|
131
|
+
if (current.primary !== undefined)
|
|
132
|
+
state = { ...state, primary: current.primary };
|
|
133
|
+
if (current.menu !== undefined)
|
|
134
|
+
state = { ...state, menu: current.menu };
|
|
135
|
+
if (current.status !== undefined)
|
|
136
|
+
state = { ...state, status: current.status };
|
|
137
|
+
report(navigation, state);
|
|
138
|
+
}, [navigation, canGoBack, title, lists]);
|
|
103
139
|
}
|
|
104
140
|
function report(navigation, state) {
|
|
105
141
|
navigation.publish(state).catch((cause) => {
|