@hosty-sdk/app 0.26.0 → 0.27.23
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +158 -8
- package/dist/index.d.ts +9 -6
- package/dist/index.js +10 -1
- package/dist/next.d.ts +4 -0
- package/dist/next.js +29 -0
- package/dist/overlay-react.d.ts +7 -1
- package/dist/overlay-react.js +2 -2
- package/dist/react.d.ts +1 -0
- package/dist/react.js +3 -0
- package/dist/ui-composition.d.ts +71 -0
- package/dist/ui-composition.js +232 -0
- package/dist/ui-context.d.ts +2 -0
- package/dist/ui-context.js +1 -0
- package/dist/ui-embedded.d.ts +18 -1
- package/dist/ui-embedded.js +123 -17
- package/dist/ui-frame-layer.d.ts +36 -0
- package/dist/ui-frame-layer.js +88 -0
- package/dist/ui-frame.d.ts +2 -0
- package/dist/ui-frame.js +43 -7
- package/dist/ui-host.d.ts +62 -15
- package/dist/ui-host.js +666 -76
- package/dist/ui-navigation-react.d.ts +17 -0
- package/dist/ui-navigation-react.js +89 -0
- package/dist/ui-react.d.ts +15 -3
- package/dist/ui-react.js +131 -54
- package/dist/ui-server.d.ts +3 -1
- package/dist/ui-server.js +14 -2
- package/dist/ui-transport.d.ts +2 -2
- package/dist/ui-transport.js +1 -1
- package/dist/ui-types.d.ts +88 -0
- package/dist/ui-types.js +1 -0
- package/dist/ui-workspaces.d.ts +54 -0
- package/dist/ui-workspaces.js +3 -0
- package/dist/ui.d.ts +7 -1
- package/dist/ui.js +22 -0
- package/package.json +5 -1
package/dist/ui-frame.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { type HostyResolvedTheme, type HostyThemePreference } from "./theme.js";
|
|
2
|
+
import type { UiViewDescriptor } from "./ui-workspaces.js";
|
|
2
3
|
import type { UiFrameSurface } from "./ui-types.js";
|
|
3
4
|
export declare function parseFrameMcpReview(event: {
|
|
4
5
|
source: unknown;
|
|
@@ -10,6 +11,7 @@ export declare function parseFrameMcpReview(event: {
|
|
|
10
11
|
} | null;
|
|
11
12
|
export declare function frameSandbox(policy: UiFrameSurface["framePolicy"]): string;
|
|
12
13
|
export type HostyAppFrameProps = {
|
|
14
|
+
view?: UiViewDescriptor;
|
|
13
15
|
surface: UiFrameSurface;
|
|
14
16
|
title: string;
|
|
15
17
|
src?: string;
|
package/dist/ui-frame.js
CHANGED
|
@@ -5,6 +5,8 @@ import { UiHostContext } from "./ui-context.js";
|
|
|
5
5
|
import { appendThemeLaunchParams, parseActiveFrameAskAssistant, parseActiveFrameAttention, parseActiveFrameAuthRequired, parseActiveFramePermissionReview } from "./embedder.js";
|
|
6
6
|
import { createShellThemeMessage } from "./theme.js";
|
|
7
7
|
import { permissionReviewUrl } from "./permissions.js";
|
|
8
|
+
import { withChromeHints } from "./ui-composition.js";
|
|
9
|
+
import { createPresentationRequestId } from "./ui.js";
|
|
8
10
|
export function parseFrameMcpReview(event, frame, src, appId) {
|
|
9
11
|
if (!frame || event.source !== frame || !event.data || typeof event.data !== "object")
|
|
10
12
|
return null;
|
|
@@ -24,7 +26,11 @@ export function frameSandbox(policy) {
|
|
|
24
26
|
}
|
|
25
27
|
/** Shared frame coordinator. No delegated-token responder and no authority inferred from messages. */
|
|
26
28
|
export function HostyAppFrame(props) {
|
|
27
|
-
const {
|
|
29
|
+
const { title, className, standaloneHref, theme, themePreference } = props;
|
|
30
|
+
const inputId = JSON.stringify([props.surface, props.src]);
|
|
31
|
+
const [destination, setDestination] = useState(null);
|
|
32
|
+
const surface = destination?.inputId === inputId ? destination.surface : props.surface;
|
|
33
|
+
const [defaultViewId] = useState(createPresentationRequestId);
|
|
28
34
|
const host = useContext(UiHostContext);
|
|
29
35
|
const ref = useRef(null);
|
|
30
36
|
const callbacks = useRef(props);
|
|
@@ -47,20 +53,44 @@ export function HostyAppFrame(props) {
|
|
|
47
53
|
return () => observer.disconnect();
|
|
48
54
|
}, []);
|
|
49
55
|
const resolvedTheme = theme ?? appearance.theme, preference = themePreference ?? appearance.preference;
|
|
50
|
-
const source =
|
|
56
|
+
const source = destination?.inputId === inputId ? new URL(surface.route, surface.origin).href
|
|
57
|
+
: props.src ?? new URL(surface.route, surface.origin).href;
|
|
51
58
|
// Theme changes are messages; they must not replace a document with an unsent draft.
|
|
52
|
-
const [launch] = useState(() => {
|
|
59
|
+
const [launch, setLaunch] = useState(() => {
|
|
53
60
|
const url = new URL(source);
|
|
54
61
|
url.searchParams.set("hosty_launch", "embedded");
|
|
55
|
-
return appendThemeLaunchParams(url.href,
|
|
62
|
+
return appendThemeLaunchParams(withChromeHints(url.href, host?.getViewChrome?.(props.view?.viewId ?? defaultViewId)
|
|
63
|
+
?? props.view?.chrome ?? { navigation: "hide", tools: { launcher: "hide" } }), resolvedTheme, preference);
|
|
56
64
|
});
|
|
57
65
|
const frameId = JSON.stringify([surface, source]);
|
|
58
66
|
useLayoutEffect(() => {
|
|
59
67
|
const frame = ref.current;
|
|
60
68
|
if (!frame || !host)
|
|
61
69
|
return;
|
|
62
|
-
|
|
63
|
-
|
|
70
|
+
const view = callbacks.current.view;
|
|
71
|
+
return host.registerFrame(frame, surface, ready => setConnection(current => ({ ready, generation: current.generation + (ready ? 1 : 0) })), { viewId: defaultViewId, workspaceId: "default", chrome: { navigation: "hide", tools: { launcher: "hide" } }, ...view,
|
|
72
|
+
navigate: next => {
|
|
73
|
+
const current = callbacks.current;
|
|
74
|
+
if (current.view?.navigate)
|
|
75
|
+
return current.view.navigate(next);
|
|
76
|
+
// Replace the document through React so every message handler follows its verified origin.
|
|
77
|
+
const url = new URL(next.route, next.origin);
|
|
78
|
+
url.searchParams.set("hosty_launch", "embedded");
|
|
79
|
+
setLoaded(false);
|
|
80
|
+
setReview(null);
|
|
81
|
+
setDestination({ inputId: JSON.stringify([current.surface, current.src]), surface: { ...next, panelId: surface.panelId } });
|
|
82
|
+
setLaunch(appendThemeLaunchParams(withChromeHints(url.href, host.getViewChrome(current.view?.viewId ?? defaultViewId)
|
|
83
|
+
?? current.view?.chrome ?? { navigation: "hide", tools: { launcher: "hide" } }), resolvedTheme, preference));
|
|
84
|
+
} });
|
|
85
|
+
}, [host, frameId]);
|
|
86
|
+
const policy = JSON.stringify(props.view?.chrome ?? {});
|
|
87
|
+
const previousPolicy = useRef({ viewId: props.view?.viewId, policy });
|
|
88
|
+
useEffect(() => {
|
|
89
|
+
const previous = previousPolicy.current;
|
|
90
|
+
previousPolicy.current = { viewId: props.view?.viewId, policy };
|
|
91
|
+
if (host && props.view && ref.current && previous.viewId === props.view.viewId && previous.policy !== policy)
|
|
92
|
+
host.setViewChrome(props.view.viewId, JSON.parse(policy));
|
|
93
|
+
}, [host, props.view?.viewId, policy]);
|
|
64
94
|
useEffect(() => {
|
|
65
95
|
if (loaded)
|
|
66
96
|
ref.current?.contentWindow?.postMessage(createShellThemeMessage(resolvedTheme, preference), surface.origin);
|
|
@@ -93,9 +123,15 @@ export function HostyAppFrame(props) {
|
|
|
93
123
|
return () => window.removeEventListener("message", handle);
|
|
94
124
|
}, [host, frameId, source, surface]);
|
|
95
125
|
const outboundReady = host ? connection.ready : loaded;
|
|
126
|
+
const sent = useRef(null);
|
|
96
127
|
useEffect(() => {
|
|
97
|
-
if (outboundReady && props.outbound)
|
|
128
|
+
if (outboundReady && props.outbound) {
|
|
129
|
+
const key = `${connection.generation}:${props.outbound.nonce}`;
|
|
130
|
+
if (sent.current === key)
|
|
131
|
+
return;
|
|
98
132
|
ref.current?.contentWindow?.postMessage(props.outbound.message, surface.origin);
|
|
133
|
+
sent.current = key;
|
|
134
|
+
}
|
|
99
135
|
}, [outboundReady, connection.generation, props.outbound?.nonce, surface.origin]);
|
|
100
136
|
const insecure = typeof window !== "undefined" && window.location.protocol === "https:" && new URL(surface.origin).protocol === "http:";
|
|
101
137
|
if (insecure)
|
package/dist/ui-host.d.ts
CHANGED
|
@@ -1,17 +1,20 @@
|
|
|
1
|
+
import { UiCompositionModel } from "./ui-composition.js";
|
|
2
|
+
import { type UiPresentation, type UiWorkspaceSnapshot, type UiWorkspaceRenderer, type UiViewDescriptor, type UiPlacementResolver } from "./ui-workspaces.js";
|
|
3
|
+
export type { UiWorkspaceRenderer, UiViewDescriptor, UiPlacementResolver, UiPresentation } from "./ui-workspaces.js";
|
|
4
|
+
import { type UiChromeOverrides, type UiActionOptions, type UiActionResult } from "./ui-types.js";
|
|
1
5
|
import { type UiClient } from "./ui.js";
|
|
2
|
-
import { type PanelRef, type PanelTarget, type PresentationOptions, type PresentationResult, type UiBootstrap, type UiEnvironment, type UiEvent, type UiFrameSurface, type UiPanel
|
|
3
|
-
export type UiTool = {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
resolved: UiResolvedPanel;
|
|
7
|
-
readiness: SurfaceReadiness;
|
|
8
|
-
attention: number;
|
|
6
|
+
import { type PanelRef, type PanelTarget, type PresentationOptions, type PresentationResult, type UiBootstrap, type UiEnvironment, type UiEvent, type UiFrameSurface, type UiPanel } from "./ui-types.js";
|
|
7
|
+
export type UiTool = UiPresentation & {
|
|
8
|
+
panelSurface: UiFrameSurface;
|
|
9
|
+
navigationRoot?: string;
|
|
9
10
|
};
|
|
10
11
|
export type UiHostSnapshot = {
|
|
11
12
|
bootstrap: UiBootstrap | null;
|
|
12
13
|
catalog: UiPanel[];
|
|
13
14
|
tools: UiTool[];
|
|
14
15
|
selected: PanelRef | null;
|
|
16
|
+
selectedPresentationId?: string | null;
|
|
17
|
+
workspaces?: UiWorkspaceSnapshot[];
|
|
15
18
|
expanded: boolean;
|
|
16
19
|
width: number;
|
|
17
20
|
error: {
|
|
@@ -23,6 +26,7 @@ export type UiHostSnapshot = {
|
|
|
23
26
|
export declare function createUiHost(options?: {
|
|
24
27
|
uiPath?: string;
|
|
25
28
|
active?: boolean;
|
|
29
|
+
chrome?: UiChromeOverrides;
|
|
26
30
|
}): UiHost;
|
|
27
31
|
export declare class UiHost implements UiClient {
|
|
28
32
|
private readonly path;
|
|
@@ -33,48 +37,91 @@ export declare class UiHost implements UiClient {
|
|
|
33
37
|
private readonly listeners;
|
|
34
38
|
private readonly events;
|
|
35
39
|
private readonly frames;
|
|
40
|
+
private readonly viewChrome;
|
|
36
41
|
private readonly ledger;
|
|
37
42
|
private readonly toolQueues;
|
|
38
|
-
private
|
|
43
|
+
private readonly moves;
|
|
44
|
+
private readonly rendererWork;
|
|
45
|
+
readonly composition: UiCompositionModel;
|
|
46
|
+
private readonly workspaces;
|
|
47
|
+
private placement?;
|
|
48
|
+
private readonly actions;
|
|
49
|
+
private focusSequence;
|
|
39
50
|
private activityGeneration;
|
|
40
51
|
private activityReady;
|
|
41
52
|
private disposed;
|
|
42
|
-
private workspace;
|
|
43
53
|
private refreshing;
|
|
44
54
|
private readonly onMessage;
|
|
45
55
|
private readonly onFocus;
|
|
46
|
-
constructor(path: string, active?: boolean);
|
|
56
|
+
constructor(path: string, active?: boolean, chrome?: UiChromeOverrides);
|
|
47
57
|
getEnvironment: () => UiEnvironment;
|
|
58
|
+
getChrome: () => import("./ui-types").UiChromeSnapshot;
|
|
59
|
+
subscribeChrome: (listener: () => void) => () => void;
|
|
60
|
+
getNavigation: () => Readonly<{
|
|
61
|
+
status: "loading" | "ready" | "unavailable";
|
|
62
|
+
catalog: import("./ui-types").UiNavigationCatalog | null;
|
|
63
|
+
entries: readonly import("./ui-types").UiNavigationEntry[];
|
|
64
|
+
activeKey: string | null;
|
|
65
|
+
code?: string;
|
|
66
|
+
}>;
|
|
67
|
+
subscribeNavigation: (listener: () => void) => () => void;
|
|
48
68
|
setActive(active: boolean): void;
|
|
49
69
|
private checkActivity;
|
|
50
70
|
claimWorkspace: (owner: object) => () => void;
|
|
71
|
+
registerWorkspace: ({ workspaceId, renderer }: {
|
|
72
|
+
workspaceId: string;
|
|
73
|
+
renderer: UiWorkspaceRenderer;
|
|
74
|
+
}) => () => void;
|
|
75
|
+
private publishWorkspaces;
|
|
76
|
+
setPlacementResolver: (resolver?: UiPlacementResolver) => void;
|
|
77
|
+
getViewChrome: (viewId: string) => UiChromeOverrides | undefined;
|
|
78
|
+
setViewChrome: (viewId: string, overrides: UiChromeOverrides) => void;
|
|
79
|
+
private publishPolicy;
|
|
80
|
+
private requireTools;
|
|
81
|
+
focusPresentation: (id: string) => boolean;
|
|
82
|
+
collapseWorkspace: (id: string) => void;
|
|
83
|
+
closePresentation: (id: string) => void;
|
|
84
|
+
private releasePresentation;
|
|
85
|
+
private resetPresentations;
|
|
86
|
+
movePresentation: (id: string, workspaceId: string) => Promise<boolean>;
|
|
51
87
|
getSnapshot: () => UiHostSnapshot;
|
|
52
88
|
subscribeSnapshot: (listener: () => void) => () => void;
|
|
53
89
|
subscribe: (listener: (event: UiEvent) => void) => () => void;
|
|
54
90
|
private emit;
|
|
55
91
|
private update;
|
|
92
|
+
private invalidateRendererWork;
|
|
93
|
+
private runRenderer;
|
|
56
94
|
private setEnvironment;
|
|
57
95
|
private bootstrap;
|
|
58
96
|
refresh: () => Promise<void>;
|
|
59
97
|
openSurface: (target: PanelTarget, options: PresentationOptions) => Promise<PresentationResult>;
|
|
60
98
|
private open;
|
|
99
|
+
private pendingCount;
|
|
100
|
+
private trimLedger;
|
|
101
|
+
private peerResult;
|
|
102
|
+
private action;
|
|
103
|
+
navigate: (entryKey: string, options: UiActionOptions) => Promise<UiActionResult>;
|
|
104
|
+
openToolPicker: (options: UiActionOptions) => Promise<UiActionResult>;
|
|
105
|
+
private pickTools;
|
|
106
|
+
private navigateFrame;
|
|
61
107
|
getPresentation: (id: string, options?: {
|
|
62
108
|
signal?: AbortSignal;
|
|
63
109
|
}) => Promise<PresentationResult>;
|
|
64
110
|
private status;
|
|
65
|
-
selectPanel: (ref: PanelRef) => Promise<PresentationResult>;
|
|
66
|
-
setExpanded: (expanded: boolean) => void;
|
|
67
|
-
setWidth: (width: number) => void;
|
|
68
|
-
setAttention: (panel: PanelRef, attention: number) => void;
|
|
111
|
+
selectPanel: (ref: PanelRef, workspaceId?: string) => Promise<PresentationResult>;
|
|
112
|
+
setExpanded: (expanded: boolean, workspaceId?: string) => void;
|
|
113
|
+
setWidth: (width: number, workspaceId?: string) => void;
|
|
114
|
+
setAttention: (panel: PanelRef | string, attention: number) => void;
|
|
69
115
|
reportSurfaceState: (_state: {
|
|
70
116
|
route: string;
|
|
71
117
|
state: "ready" | "blocked";
|
|
72
118
|
code?: string;
|
|
73
119
|
}) => void;
|
|
74
120
|
private forgetFrameRequests;
|
|
75
|
-
registerFrame: (element: HTMLIFrameElement, surface: UiFrameSurface, onConnection?: (connected: boolean) => void) => (() => void);
|
|
121
|
+
registerFrame: (element: HTMLIFrameElement, surface: UiFrameSurface, onConnection?: (connected: boolean) => void, view?: UiViewDescriptor) => (() => void);
|
|
76
122
|
private post;
|
|
77
123
|
private reportFrame;
|
|
124
|
+
private reportRoute;
|
|
78
125
|
private receive;
|
|
79
126
|
dispose: () => void;
|
|
80
127
|
}
|