@hosty-sdk/app 0.31.0 → 0.34.1
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 +23 -0
- package/dist/activity-react.js +8 -8
- package/dist/app-code-exchange.d.ts +8 -0
- package/dist/app-code-exchange.js +31 -0
- package/dist/assets/jetbrains-mono-LICENSE.txt +93 -0
- package/dist/assets/jetbrains-mono-cyrillic.woff2 +0 -0
- package/dist/assets/jetbrains-mono-latin-ext.woff2 +0 -0
- package/dist/assets/jetbrains-mono-latin.woff2 +0 -0
- package/dist/assets/plex-sans-LICENSE.txt +93 -0
- package/dist/assets/plex-sans-cyrillic.woff2 +0 -0
- package/dist/assets/plex-sans-latin-ext.woff2 +0 -0
- package/dist/assets/plex-sans-latin.woff2 +0 -0
- package/dist/auth-notice.d.ts +4 -4
- package/dist/auth-notice.js +12 -44
- package/dist/browser-auth.d.ts +1 -1
- package/dist/browser-auth.js +1 -1
- package/dist/chrome-mark.d.ts +3 -0
- package/dist/chrome-mark.js +7 -0
- package/dist/core-mark-geometry.d.ts +17 -0
- package/dist/core-mark-geometry.js +8 -0
- package/dist/install-react.js +7 -3
- package/dist/overlay-react.js +41 -25
- package/dist/permissions-react.js +9 -3
- package/dist/react.d.ts +1 -1
- package/dist/react.js +10 -19
- package/dist/sandbox-react.js +20 -9
- package/dist/sandbox-server.d.ts +2 -1
- package/dist/sandbox-server.js +5 -4
- package/dist/sdk-chrome.d.ts +13 -0
- package/dist/sdk-chrome.js +102 -0
- package/dist/ui-composition.d.ts +2 -2
- package/dist/ui-embedded.d.ts +3 -2
- package/dist/ui-embedded.js +12 -3
- package/dist/ui-frame-layer.js +2 -1
- package/dist/ui-frame.js +4 -3
- package/dist/ui-host.d.ts +6 -2
- package/dist/ui-host.js +37 -5
- package/dist/ui-navigation-react.js +11 -11
- package/dist/ui-react.js +22 -18
- package/dist/ui-types.d.ts +7 -0
- package/dist/ui-types.js +5 -0
- package/dist/ui.d.ts +2 -2
- package/package.json +7 -6
package/dist/ui-embedded.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { UiCompositionModel } from "./ui-composition.js";
|
|
2
|
-
import { type UiChromeOverrides, type UiActionOptions, type UiActionResult } from "./ui-types.js";
|
|
2
|
+
import { type UiNavigationOptions, type UiChromeOverrides, type UiActionOptions, type UiActionResult } from "./ui-types.js";
|
|
3
3
|
import { type UiClient } from "./ui.js";
|
|
4
4
|
import { type PanelTarget, type PresentationOptions, type PresentationResult, type UiEnvironment, type UiEvent } from "./ui-types.js";
|
|
5
5
|
/** Direct-child transport. Discovery carries no identity and never creates a fallback host. */
|
|
@@ -19,6 +19,7 @@ export declare class EmbeddedUiClient implements UiClient {
|
|
|
19
19
|
private state;
|
|
20
20
|
private active;
|
|
21
21
|
private offered;
|
|
22
|
+
private mainNavigation;
|
|
22
23
|
private readonly onMessage;
|
|
23
24
|
private readonly onFocus;
|
|
24
25
|
private readonly onKey;
|
|
@@ -51,7 +52,7 @@ export declare class EmbeddedUiClient implements UiClient {
|
|
|
51
52
|
signal?: AbortSignal;
|
|
52
53
|
}) => Promise<PresentationResult>;
|
|
53
54
|
openToolPicker: (options: UiActionOptions) => Promise<UiActionResult>;
|
|
54
|
-
navigate: (entryKey: string, options:
|
|
55
|
+
navigate: (entryKey: string, options: UiNavigationOptions) => Promise<UiActionResult>;
|
|
55
56
|
private reportRoute;
|
|
56
57
|
reportSurfaceState: (state: {
|
|
57
58
|
route: string;
|
package/dist/ui-embedded.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { activeNavigationKey, UiCompositionModel } from "./ui-composition.js";
|
|
2
|
-
import { UI_COMPOSITION_CAPABILITY } from "./ui-types.js";
|
|
2
|
+
import { UI_COMPOSITION_CAPABILITY, UI_MAIN_NAVIGATION_CAPABILITY, navigationSearch } from "./ui-types.js";
|
|
3
3
|
import { createPresentationRequestId } from "./ui.js";
|
|
4
4
|
import { UiError } from "./ui-types.js";
|
|
5
5
|
import { canonicalSurfaceRoute, DISCOVERY_TIMEOUT, failure, readUiMessage, uiRequest, validId, validTarget, waitForPresentation } from "./ui-transport.js";
|
|
@@ -20,6 +20,7 @@ export class EmbeddedUiClient {
|
|
|
20
20
|
state = null;
|
|
21
21
|
active = true;
|
|
22
22
|
offered = false;
|
|
23
|
+
mainNavigation = false;
|
|
23
24
|
onMessage = (event) => { void this.receive(event); };
|
|
24
25
|
onFocus = () => { void this.recheck(); };
|
|
25
26
|
onKey = (event) => { if (this.active && !event.defaultPrevented && event.key === "Escape")
|
|
@@ -200,6 +201,7 @@ export class EmbeddedUiClient {
|
|
|
200
201
|
return;
|
|
201
202
|
}
|
|
202
203
|
clearTimeout(this.deadline);
|
|
204
|
+
this.mainNavigation = Array.isArray(msg.capabilities) && msg.capabilities.includes(UI_MAIN_NAVIGATION_CAPABILITY);
|
|
203
205
|
this.setEnvironment("connected");
|
|
204
206
|
if (Array.isArray(msg.capabilities) && msg.capabilities.includes(UI_COMPOSITION_CAPABILITY)) {
|
|
205
207
|
this.composition.setHostPolicy(channel.generation, msg.revision, msg.chrome, msg.rootDisabled === true, msg.endpoint);
|
|
@@ -234,7 +236,7 @@ export class EmbeddedUiClient {
|
|
|
234
236
|
if (signal?.aborted)
|
|
235
237
|
return { status: "uncertain", requestId, code: "presentation_unknown" };
|
|
236
238
|
const target = message.target;
|
|
237
|
-
const input = message.type === "hosty:ui:status" ? null : JSON.stringify([message.type, target?.panel ?? null, target?.route ?? null, message.reuse ?? "exact", message.entryKey ?? null]);
|
|
239
|
+
const input = message.type === "hosty:ui:status" ? null : JSON.stringify([message.type, target?.panel ?? null, target?.route ?? null, message.reuse ?? "exact", message.entryKey ?? null, message.search ?? null]);
|
|
238
240
|
if (this.localActions.has(requestId))
|
|
239
241
|
return { status: "rejected", requestId, code: "request_conflict" };
|
|
240
242
|
const existing = this.pending.get(requestId);
|
|
@@ -255,7 +257,7 @@ export class EmbeddedUiClient {
|
|
|
255
257
|
return { status: "blocked", requestId, code: "presentation_busy" };
|
|
256
258
|
let settle;
|
|
257
259
|
const operation = new Promise(resolve => { settle = resolve; });
|
|
258
|
-
const entry = { kind: message.type === "hosty:ui:picker" || message.type === "hosty:ui:navigate" ? "action" : "presentation", input, operation, waiters: 0, sent: false, resolve: result => {
|
|
260
|
+
const entry = { kind: message.type === "hosty:ui:picker" || message.type === "hosty:ui:navigate" || message.type === "hosty:ui:navigate-main" ? "action" : "presentation", input, operation, waiters: 0, sent: false, resolve: result => {
|
|
259
261
|
if (this.pending.get(requestId) === entry)
|
|
260
262
|
this.pending.delete(requestId);
|
|
261
263
|
settle(result);
|
|
@@ -302,6 +304,13 @@ export class EmbeddedUiClient {
|
|
|
302
304
|
return this.request(options.requestId, { type: "hosty:ui:picker" }, options.signal);
|
|
303
305
|
};
|
|
304
306
|
navigate = (entryKey, options) => {
|
|
307
|
+
if (options.placement === "main") {
|
|
308
|
+
if (!navigationSearch(options.search))
|
|
309
|
+
return Promise.resolve({ status: "rejected", requestId: options.requestId, code: "destination_invalid" });
|
|
310
|
+
if (!this.mainNavigation)
|
|
311
|
+
return Promise.resolve({ status: "unsupported", requestId: options.requestId, code: "main_navigation_unavailable" });
|
|
312
|
+
return this.request(options.requestId, { type: "hosty:ui:navigate-main", entryKey, search: options.search }, options.signal);
|
|
313
|
+
}
|
|
305
314
|
const id = options.requestId, existing = this.localActions.get(id);
|
|
306
315
|
if (!validId(id))
|
|
307
316
|
return Promise.resolve({ status: "rejected", requestId: id, code: "invalid_presentation" });
|
package/dist/ui-frame-layer.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
3
|
+
import { SdkChromeStyles } from "./sdk-chrome.js";
|
|
3
4
|
import { createContext, useEffect, useLayoutEffect, useSyncExternalStore } from "react";
|
|
4
5
|
import { HostyAppFrame } from "./ui-frame.js";
|
|
5
6
|
import { createPortal } from "react-dom";
|
|
@@ -83,6 +84,6 @@ export function HostyPresentationLayer({ layer, host, active }) {
|
|
|
83
84
|
return createPortal(_jsxs(_Fragment, { children: [viewport?.drawer && _jsx("span", { tabIndex: 0, "aria-hidden": "true", className: "hosty-panel-focus-guard", onFocus: () => focusControls(true) }), surface ? _jsx(HostyAppFrame, { surface: { ...surface, panelId: tool.target.panel.panelId }, view: { viewId: `tool-${tool.presentationId}`, workspaceId: tool.workspaceId, presentationId: tool.presentationId, chrome: { navigation: "hide", tools: { launcher: "hide" } } }, title: state.catalog.find(p => panelKey(p.panel) === tool.key)?.label ?? "Tool", standaloneHref: tool.resolved.standaloneHref, onAskAssistant: viewport?.onAskAssistant, onAuthRequired: viewport?.onAuthRequired, onAttention: count => host.setAttention(tool.presentationId, count), outbound: viewport?.outbound && panelKey(viewport.outbound.target.panel) === tool.key
|
|
84
85
|
&& (viewport.outbound.presentationId ? viewport.outbound.presentationId === tool.presentationId
|
|
85
86
|
: workspace?.selectedId === tool.presentationId && viewport.outbound.target.route === tool.currentRoute) ? viewport.outbound : null })
|
|
86
|
-
: _jsxs("div", { className: "hosty-panel-notice", role: "status", children: ["This tool is unavailable (", tool.resolved.state, ")."] }), viewport?.drawer && _jsx("span", { tabIndex: 0, "aria-hidden": "true", className: "hosty-panel-focus-guard", onFocus: () => focusControls(false) })] }), container, tool.presentationId);
|
|
87
|
+
: _jsxs("div", { "data-hosty-sdk": true, className: "hosty-panel-notice", role: "status", children: [_jsx(SdkChromeStyles, {}), "This tool is unavailable (", tool.resolved.state, ")."] }), viewport?.drawer && _jsx("span", { tabIndex: 0, "aria-hidden": "true", className: "hosty-panel-focus-guard", onFocus: () => focusControls(false) })] }), container, tool.presentationId);
|
|
87
88
|
})] });
|
|
88
89
|
}
|
package/dist/ui-frame.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import {
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
3
|
+
import { SdkChromeStyles } from "./sdk-chrome.js";
|
|
3
4
|
import { useContext, useEffect, useLayoutEffect, useRef, useState } from "react";
|
|
4
5
|
import { UiHostContext } from "./ui-context.js";
|
|
5
6
|
import { appendThemeLaunchParams, parseActiveFrameAskAssistant, parseActiveFrameAttention, parseActiveFrameAuthRequired, parseActiveFramePermissionReview } from "./embedder.js";
|
|
@@ -135,6 +136,6 @@ export function HostyAppFrame(props) {
|
|
|
135
136
|
}, [outboundReady, connection.generation, props.outbound?.nonce, surface.origin]);
|
|
136
137
|
const insecure = typeof window !== "undefined" && window.location.protocol === "https:" && new URL(surface.origin).protocol === "http:";
|
|
137
138
|
if (insecure)
|
|
138
|
-
return _jsxs("div", { role: "status", className: "hosty-panel-notice", children: [_jsxs("strong", { children: [title, " needs HTTPS to appear here"] }), _jsx("p", { children: "This app publishes an HTTP address. Configure its public HTTPS origin in Hosty." }), /^(localhost|127\.|\[::1\])/.test(new URL(surface.origin).hostname) && _jsx("p", { children: "This local address is reachable only from the Hosty computer." }), standaloneHref && _jsx("a", { href: standaloneHref, target: "_blank", rel: "noopener noreferrer", children: "Open separately" })] });
|
|
139
|
-
return _jsxs(_Fragment, { children: [review && _jsx("a", { className: "hosty-panel-review", href: review, target: "_blank", rel: "noopener noreferrer", children: "Review access in Core" }), _jsx("iframe", { ref: ref, className: className, title: title, src: launch, sandbox: frameSandbox(surface.framePolicy), allow: surface.framePolicy.microphone ? "clipboard-write; microphone 'src'" : "clipboard-write", style: { border: 0, height: "100%", width: "100%", colorScheme: resolvedTheme }, onLoad: () => setLoaded(true) })] });
|
|
139
|
+
return _jsxs("div", { "data-hosty-sdk": true, role: "status", className: "hosty-panel-notice", children: [_jsx(SdkChromeStyles, {}), _jsxs("strong", { children: [title, " needs HTTPS to appear here"] }), _jsx("p", { children: "This app publishes an HTTP address. Configure its public HTTPS origin in Hosty." }), /^(localhost|127\.|\[::1\])/.test(new URL(surface.origin).hostname) && _jsx("p", { children: "This local address is reachable only from the Hosty computer." }), standaloneHref && _jsx("a", { href: standaloneHref, target: "_blank", rel: "noopener noreferrer", children: "Open separately" })] });
|
|
140
|
+
return _jsxs(_Fragment, { children: [review && _jsxs(_Fragment, { children: [_jsx(SdkChromeStyles, {}), _jsx("a", { "data-hosty-sdk": true, className: "hosty-panel-review", href: review, target: "_blank", rel: "noopener noreferrer", children: "Review access in Core" })] }), _jsx("iframe", { ref: ref, className: className, title: title, src: launch, sandbox: frameSandbox(surface.framePolicy), allow: surface.framePolicy.microphone ? "clipboard-write; microphone 'src'" : "clipboard-write", style: { border: 0, height: "100%", width: "100%", colorScheme: resolvedTheme }, onLoad: () => setLoaded(true) })] });
|
|
140
141
|
}
|
package/dist/ui-host.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { UiCompositionModel } from "./ui-composition.js";
|
|
2
2
|
import { type UiPresentation, type UiWorkspaceSnapshot, type UiWorkspaceRenderer, type UiViewDescriptor, type UiPlacementResolver } from "./ui-workspaces.js";
|
|
3
3
|
export type { UiWorkspaceRenderer, UiViewDescriptor, UiPlacementResolver, UiPresentation } from "./ui-workspaces.js";
|
|
4
|
-
import { type UiChromeOverrides, type UiActionOptions, type UiActionResult } from "./ui-types.js";
|
|
4
|
+
import { type UiNavigationOptions, type UiChromeOverrides, type UiActionOptions, type UiActionResult } from "./ui-types.js";
|
|
5
5
|
import { type UiClient } from "./ui.js";
|
|
6
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
7
|
export type UiTool = UiPresentation & {
|
|
@@ -45,6 +45,9 @@ export declare class UiHost implements UiClient {
|
|
|
45
45
|
readonly composition: UiCompositionModel;
|
|
46
46
|
private readonly workspaces;
|
|
47
47
|
private placement?;
|
|
48
|
+
private mainNavigator?;
|
|
49
|
+
/** The host chooses where its primary application area lives. */
|
|
50
|
+
setMainNavigator(navigate?: (surface: UiFrameSurface) => Promise<boolean>): void;
|
|
48
51
|
private readonly actions;
|
|
49
52
|
private focusSequence;
|
|
50
53
|
private activityGeneration;
|
|
@@ -100,9 +103,10 @@ export declare class UiHost implements UiClient {
|
|
|
100
103
|
private trimLedger;
|
|
101
104
|
private peerResult;
|
|
102
105
|
private action;
|
|
103
|
-
navigate: (entryKey: string, options:
|
|
106
|
+
navigate: (entryKey: string, options: UiNavigationOptions) => Promise<UiActionResult>;
|
|
104
107
|
openToolPicker: (options: UiActionOptions) => Promise<UiActionResult>;
|
|
105
108
|
private pickTools;
|
|
109
|
+
private navigateMain;
|
|
106
110
|
private navigateFrame;
|
|
107
111
|
getPresentation: (id: string, options?: {
|
|
108
112
|
signal?: AbortSignal;
|
package/dist/ui-host.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { UiCompositionModel, validChromeOverrides, withChromeHints } from "./ui-composition.js";
|
|
2
2
|
import { DEFAULT_WORKSPACE, ROOT_VIEW, workspace } from "./ui-workspaces.js";
|
|
3
|
-
import { UI_COMPOSITION_CAPABILITY } from "./ui-types.js";
|
|
3
|
+
import { UI_COMPOSITION_CAPABILITY, UI_MAIN_NAVIGATION_CAPABILITY, navigationSearch } from "./ui-types.js";
|
|
4
4
|
import { createPresentationRequestId } from "./ui.js";
|
|
5
5
|
import { UiError, panelKey, routeWithin } from "./ui-types.js";
|
|
6
6
|
import { canonicalSurfaceRoute, DISCOVERY_TIMEOUT, failure, readUiMessage, uiPath, uiRequest, validId, validTarget, waitForPresentation } from "./ui-transport.js";
|
|
@@ -40,6 +40,9 @@ export class UiHost {
|
|
|
40
40
|
composition;
|
|
41
41
|
workspaces = new Map([[DEFAULT_WORKSPACE, workspace(DEFAULT_WORKSPACE)]]);
|
|
42
42
|
placement;
|
|
43
|
+
mainNavigator;
|
|
44
|
+
/** The host chooses where its primary application area lives. */
|
|
45
|
+
setMainNavigator(navigate) { this.mainNavigator = navigate; }
|
|
43
46
|
actions = new Map();
|
|
44
47
|
focusSequence = 0;
|
|
45
48
|
activityGeneration = 0;
|
|
@@ -628,7 +631,9 @@ export class UiHost {
|
|
|
628
631
|
void operation.then(result => { entry.pending = undefined; entry.result = result; this.trimLedger(source); });
|
|
629
632
|
return waitForPresentation(operation, id, options.signal);
|
|
630
633
|
}
|
|
631
|
-
navigate = (entryKey, options) => this.action("navigate", entryKey, options, async (check) => {
|
|
634
|
+
navigate = (entryKey, options) => this.action("navigate", [entryKey, options.placement, options.search], options, async (check) => {
|
|
635
|
+
if (options.placement === "main")
|
|
636
|
+
throw new UiError("main_navigation_unavailable", "This document has no parent main surface.");
|
|
632
637
|
const catalog = this.getNavigation().catalog, entry = catalog?.entries.find(e => e.key === entryKey);
|
|
633
638
|
if (!entry || !entry.route)
|
|
634
639
|
throw new UiError("destination_changed", "Refresh this app's navigation.");
|
|
@@ -666,6 +671,31 @@ export class UiHost {
|
|
|
666
671
|
checkRenderer();
|
|
667
672
|
}, frame);
|
|
668
673
|
}
|
|
674
|
+
navigateMain(entryKey, search, options, frame) {
|
|
675
|
+
return this.action("navigate-main", [entryKey, search], options, async (check) => {
|
|
676
|
+
if (!navigationSearch(search))
|
|
677
|
+
throw new UiError("destination_invalid", "Invalid navigation query.");
|
|
678
|
+
const navigate = this.mainNavigator;
|
|
679
|
+
if (!navigate)
|
|
680
|
+
throw new UiError("main_navigation_unavailable", "This host has no main application surface.");
|
|
681
|
+
await this.bootstrap();
|
|
682
|
+
check();
|
|
683
|
+
const target = { appId: frame.surface.appId, installationId: frame.surface.installationId, entryKey };
|
|
684
|
+
const resolved = await uiRequest(this.path, "navigation/resolve", target);
|
|
685
|
+
check();
|
|
686
|
+
if (!resolved.available || !resolved.surface)
|
|
687
|
+
throw new UiError(resolved.state, "This page is unavailable.");
|
|
688
|
+
const destination = { ...resolved.surface };
|
|
689
|
+
if (search !== undefined) {
|
|
690
|
+
const url = new URL(destination.route, destination.origin);
|
|
691
|
+
url.search = search;
|
|
692
|
+
destination.route = url.pathname + url.search + url.hash;
|
|
693
|
+
}
|
|
694
|
+
if (!await this.mainNavigator?.(destination))
|
|
695
|
+
throw new UiError("destination_changed", "Refresh this app's navigation.");
|
|
696
|
+
check();
|
|
697
|
+
}, frame);
|
|
698
|
+
}
|
|
669
699
|
navigateFrame(entryKey, options, frame) {
|
|
670
700
|
return this.action("navigate", entryKey, options, async (check) => {
|
|
671
701
|
await this.bootstrap();
|
|
@@ -951,7 +981,7 @@ export class UiHost {
|
|
|
951
981
|
}
|
|
952
982
|
this.post(frame, { type: "hosty:ui:connected", coreOrigin: own.coreOrigin, userId: own.userId,
|
|
953
983
|
appId: own.app.appId, installationId: own.app.installationId, code: frame.connected ? null : "actor_mismatch", route: frame.surface.route,
|
|
954
|
-
...(frame.composition ? { capabilities: [UI_COMPOSITION_CAPABILITY], chrome: frame.chrome, revision: ++frame.policyRevision,
|
|
984
|
+
...(frame.composition ? { capabilities: [UI_COMPOSITION_CAPABILITY, UI_MAIN_NAVIGATION_CAPABILITY], chrome: frame.chrome, revision: ++frame.policyRevision,
|
|
955
985
|
rootDisabled: !this.getChrome().tools.enabled, endpoint: frame.surface.endpoint } : {}) });
|
|
956
986
|
if (frame.connected !== wasConnected)
|
|
957
987
|
frame.onConnection?.(frame.connected);
|
|
@@ -962,9 +992,11 @@ export class UiHost {
|
|
|
962
992
|
if (!frame.connected)
|
|
963
993
|
return;
|
|
964
994
|
const source = `${frame.surface.appId}:${frame.generation}:${frame.nonce}`;
|
|
965
|
-
if (frame.composition && (msg.type === "hosty:ui:picker" || msg.type === "hosty:ui:navigate") && validId(msg.requestId)) {
|
|
995
|
+
if (frame.composition && (msg.type === "hosty:ui:picker" || msg.type === "hosty:ui:navigate" || msg.type === "hosty:ui:navigate-main") && validId(msg.requestId)) {
|
|
966
996
|
const result = msg.type === "hosty:ui:picker" ? await this.pickTools({ requestId: msg.requestId }, frame)
|
|
967
|
-
: typeof msg.entryKey === "string" ?
|
|
997
|
+
: typeof msg.entryKey === "string" ? msg.type === "hosty:ui:navigate-main"
|
|
998
|
+
? await this.navigateMain(msg.entryKey, msg.search, { requestId: msg.requestId }, frame)
|
|
999
|
+
: await this.navigateFrame(msg.entryKey, { requestId: msg.requestId }, frame)
|
|
968
1000
|
: { status: "rejected", requestId: msg.requestId, code: "destination_invalid" };
|
|
969
1001
|
if (source === `${frame.surface.appId}:${frame.generation}:${frame.nonce}`)
|
|
970
1002
|
this.post(frame, { type: "hosty:ui:action-result", requestId: msg.requestId, result });
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
3
3
|
import { useContext, useEffect, useLayoutEffect, useRef, useState, useSyncExternalStore } from "react";
|
|
4
|
+
import { SdkChromeStyles, SdkIcon } from "./sdk-chrome.js";
|
|
4
5
|
import { ui, createPresentationRequestId } from "./ui.js";
|
|
5
6
|
import { INITIAL_CHROME, INITIAL_NAVIGATION } from "./ui-composition.js";
|
|
6
7
|
import { UiCompositionContext } from "./ui-context.js";
|
|
@@ -22,7 +23,7 @@ export function HostyToolLauncher() {
|
|
|
22
23
|
if (chrome.tools.owner === "local" || !chrome.tools.enabled || chrome.tools.launcher === "hide"
|
|
23
24
|
|| !chrome.capabilities.includes(UI_COMPOSITION_CAPABILITY))
|
|
24
25
|
return null;
|
|
25
|
-
return _jsxs("div", { className: "hosty-child-tools", children: [_jsx("button", { type: "button", disabled: chrome.tools.owner !== "host", onClick: async () => {
|
|
26
|
+
return _jsxs("div", { "data-hosty-sdk": true, className: "hosty-child-tools", children: [_jsx(SdkChromeStyles, {}), _jsx("style", { children: navigationStyles }), _jsx("button", { type: "button", disabled: chrome.tools.owner !== "host", onClick: async () => {
|
|
26
27
|
const result = await ui.openToolPicker({ requestId: createPresentationRequestId() });
|
|
27
28
|
setError(result.status === "accepted" ? null : "Tools are unavailable. Reconnect to your host or open the app separately.");
|
|
28
29
|
}, children: "Tools" }), error && _jsx("span", { role: "status", children: error })] });
|
|
@@ -71,19 +72,18 @@ export function HostyNavigation({ className = "" }) {
|
|
|
71
72
|
}, children: [entry.icon && _jsx("img", { src: entry.icon, alt: "", width: 18, height: 18 }), _jsx("span", { children: entry.label })] }, entry.key)), error && _jsx("p", { role: "status", children: error })] });
|
|
72
73
|
if (empty)
|
|
73
74
|
return null;
|
|
74
|
-
return _jsxs(HostyNavigationBoundary, { children: [_jsx("style", { children: navigationStyles }), _jsx("div", { className: `hosty-navigation ${className}`, children: narrow ? _jsxs(_Fragment, { children: [_jsx("button", { ref: opener, type: "button", "aria-label": "Page navigation", "aria-expanded": expanded, onClick: () => setExpanded(true), children: "
|
|
75
|
+
return _jsxs(HostyNavigationBoundary, { children: [_jsx(SdkChromeStyles, {}), _jsx("style", { children: navigationStyles }), _jsx("div", { "data-hosty-sdk": true, className: `hosty-navigation ${className}`, children: narrow ? _jsxs(_Fragment, { children: [_jsx("button", { ref: opener, type: "button", "aria-label": "Page navigation", "aria-expanded": expanded, onClick: () => setExpanded(true), children: _jsx(SdkIcon, { name: "menu" }) }), _jsxs("dialog", { "data-hosty-sdk": true, ref: dialog, className: "hosty-navigation-dialog", "aria-label": "Page navigation", onCancel: () => setExpanded(false), children: [_jsx("button", { type: "button", onClick: () => setExpanded(false), children: "Close navigation" }), _jsx("nav", { "aria-label": "App pages", children: links })] })] }) : _jsx("nav", { "aria-label": "App pages", children: links }) })] });
|
|
75
76
|
}
|
|
76
77
|
const navigationStyles = `
|
|
77
78
|
[data-hosty-navigation=hide] [data-hosty-navigation-boundary]{display:none!important}
|
|
78
|
-
.hosty-app-layout{display:flex;width:100%;height:100%;min-height:0;min-width:0}
|
|
79
|
-
.hosty-
|
|
80
|
-
.hosty-navigation{flex:0 0 190px;box-sizing:border-box;padding:12px;border-right:1px solid color-mix(in srgb,currentColor 14%,transparent);font:14px system-ui}
|
|
79
|
+
.hosty-app-layout{display:flex;width:100%;height:100%;min-height:0;min-width:0}.hosty-app-content{flex:1;min-width:0;min-height:0;overflow:auto;outline:none}
|
|
80
|
+
.hosty-navigation{flex:0 0 190px;box-sizing:border-box;padding:12px;border-right:1px solid var(--hs-border);background:var(--hs-bg)}
|
|
81
81
|
.hosty-navigation nav,.hosty-navigation-dialog nav{display:flex;flex-direction:column;gap:4px}
|
|
82
|
-
.hosty-navigation a,.hosty-navigation-dialog a{display:flex;align-items:center;gap:8px;padding:
|
|
83
|
-
.hosty-navigation a[aria-current=page],.hosty-navigation-dialog a[aria-current=page]{background:
|
|
84
|
-
.hosty-navigation a[aria-disabled=true]{opacity:.
|
|
85
|
-
.hosty-navigation button,.hosty-child-tools button,.hosty-navigation-dialog button{font:
|
|
86
|
-
.hosty-navigation-dialog{position:fixed;inset:0 auto 0 0;margin:0;height:100dvh;width:min(300px,85vw);box-sizing:border-box;border:0;padding:16px;background:
|
|
87
|
-
.hosty-navigation-dialog::backdrop{background:#
|
|
82
|
+
.hosty-navigation a,.hosty-navigation-dialog a{display:flex;align-items:center;gap:8px;padding:8px 10px;border-radius:8px;text-decoration:none;color:inherit;font:500 14px/20px var(--hs-font);min-width:0;transition:background-color 120ms ease-out}.hosty-navigation a>span,.hosty-navigation-dialog a>span{min-width:0;overflow-wrap:anywhere}.hosty-navigation a>img,.hosty-navigation-dialog a>img{flex:none}
|
|
83
|
+
.hosty-navigation a:hover,.hosty-navigation-dialog a:hover{background:var(--hs-brand-soft)}.hosty-navigation a[aria-current=page],.hosty-navigation-dialog a[aria-current=page]{background:var(--hs-brand-soft);font-weight:500}
|
|
84
|
+
.hosty-navigation a[aria-disabled=true],.hosty-navigation-dialog a[aria-disabled=true]{opacity:.5;cursor:not-allowed}
|
|
85
|
+
.hosty-navigation button,.hosty-child-tools button,.hosty-navigation-dialog button{display:inline-flex;align-items:center;justify-content:center;gap:8px;font:500 14px/20px var(--hs-font);color:inherit;background:var(--hs-card);border:1px solid var(--hs-input);border-radius:8px;padding:7px 10px;cursor:pointer}.hosty-navigation button:hover,.hosty-child-tools button:hover,.hosty-navigation-dialog button:hover{background:var(--hs-brand-soft)}.hosty-child-tools button:disabled{opacity:.5;cursor:not-allowed}
|
|
86
|
+
.hosty-navigation-dialog{position:fixed;inset:0 auto 0 0;margin:0;height:100dvh;max-height:none;width:min(300px,85vw);box-sizing:border-box;border:0;border-right:1px solid var(--hs-border);padding:16px;background:var(--hs-card);color:var(--hs-fg)}.hosty-navigation-dialog>button{margin-bottom:16px}
|
|
87
|
+
.hosty-navigation-dialog::backdrop{background:#0F1B2D57}.hosty-child-tools{padding:6px 10px}.hosty-child-tools>[role=status]{display:inline-block;margin:0 8px;color:var(--hs-muted);font-size:13px}
|
|
88
88
|
@media(max-width:899px){.hosty-app-layout{flex-direction:column}.hosty-navigation{flex:0 0 auto;border-right:0;padding:6px 10px}}
|
|
89
89
|
`;
|
package/dist/ui-react.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
3
|
+
import { SdkChromeStyles, SdkIcon } from "./sdk-chrome.js";
|
|
3
4
|
import { useContext, useEffect, useId, useLayoutEffect, useRef, useState, useSyncExternalStore } from "react";
|
|
4
5
|
import { bindUiClient, ui } from "./ui.js";
|
|
5
6
|
import { createUiHost } from "./ui-host.js";
|
|
@@ -52,7 +53,7 @@ export function HostyUiRoot({ children, uiPath, panelLayout = "auto", active = t
|
|
|
52
53
|
if (allowed)
|
|
53
54
|
mounted.current = true;
|
|
54
55
|
const content = _jsxs("div", { className: "hosty-app-layout", children: [navigation?.renderer !== "custom" && _jsx(HostyNavigation, {}), _jsxs("div", { className: "hosty-app-content", "data-hosty-content": true, tabIndex: -1, children: [_jsx(HostyToolLauncher, {}), children] })] });
|
|
55
|
-
return _jsx(UiHostContext.Provider, { value: host, children: _jsx(UiCompositionContext.Provider, { value: clientRef.current?.composition ?? null, children: _jsxs(UiFrameLayerContext.Provider, { value: layer, children: [_jsx("style", { children: `.hosty-app-layout{display:flex;width:100%;height:100%;min-width:0;min-height:0}.hosty-app-content{flex:1;min-width:0;min-height:0;overflow:auto;outline:none}` }), _jsx("div", { hidden: !allowed, inert: !allowed, style: { display: allowed ? "contents" : "none" }, children: mounted.current && (panelLayout === "auto" && host ? _jsx(HostyPanelWorkspace, { children: content }) : content) }), !allowed &&
|
|
56
|
+
return _jsx(UiHostContext.Provider, { value: host, children: _jsx(UiCompositionContext.Provider, { value: clientRef.current?.composition ?? null, children: _jsxs(UiFrameLayerContext.Provider, { value: layer, children: [_jsx("style", { children: `.hosty-app-layout{display:flex;width:100%;height:100%;min-width:0;min-height:0}.hosty-app-content{flex:1;min-width:0;min-height:0;overflow:auto;outline:none}` }), _jsx("div", { hidden: !allowed, inert: !allowed, style: { display: allowed ? "contents" : "none" }, children: mounted.current && (panelLayout === "auto" && host ? _jsx(HostyPanelWorkspace, { children: content }) : content) }), !allowed && _jsxs("div", { "data-hosty-sdk": true, role: "status", className: "hosty-panel-notice", children: [_jsx(SdkChromeStyles, {}), environment?.connection === "blocked" ? _jsxs(_Fragment, { children: [_jsx("strong", { children: environment.code === "actor_mismatch" ? "Use the same Hosty account in this app and its host" : "Cannot verify this app’s host" }), _jsx("p", { children: "Check your account and access in Hosty Core, then reconnect. Your existing discussion is unchanged." }), _jsx("button", { type: "button", onClick: () => window.location.reload(), children: "Reconnect" })] }) : "Connecting to the workspace…"] }), host && _jsx(HostyPresentationLayer, { layer: layer, host: host, active: !!allowed })] }) }) });
|
|
56
57
|
}
|
|
57
58
|
const empty = { bootstrap: null, catalog: [], tools: [], selected: null, expanded: false, width: 360, error: null };
|
|
58
59
|
const noop = () => () => { };
|
|
@@ -243,14 +244,14 @@ export function HostyPanelWorkspace({ children, workspaceId = DEFAULT_WORKSPACE,
|
|
|
243
244
|
handle.addEventListener("pointerup", end);
|
|
244
245
|
handle.addEventListener("lostpointercapture", end);
|
|
245
246
|
};
|
|
246
|
-
return _jsxs("div", { ref: root, className: `hosty-panel-workspace ${className}`, "data-hosty-panel-layout": drawer ? "drawer" : "dock", style: { "--hosty-panel-width": `${width}px`, ...(unsupported ? { display: "contents" } : {}) }, children: [_jsx("style", { children: styles }), _jsx("div", { ref: content, className: "hosty-panel-document", style: unsupported ? { display: "contents" } : undefined, inert: expanded && drawer, children: children }), _jsxs("nav", { className: "hosty-panel-rail", "aria-label": "Tools", style: unsupported || !chrome.tools.enabled || chrome.tools.launcher === "hide" ? { display: "none" } : undefined, inert: unsupported || expanded && drawer, children: [tabs.map((panel, index) => _jsxs("button", { type: "button", title: `${panel.appName}: ${panel.label}`, "aria-label": panel.label, "aria-controls": bodyId, "aria-expanded": expanded && selectedKey === panelKey(panel.panel), onClick: () => void select(panel), onKeyDown: event => {
|
|
247
|
+
return _jsxs("div", { ref: root, className: `hosty-panel-workspace ${className}`, "data-hosty-panel-layout": drawer ? "drawer" : "dock", style: { "--hosty-panel-width": `${width}px`, ...(unsupported ? { display: "contents" } : {}) }, children: [_jsx(SdkChromeStyles, {}), _jsx("style", { children: styles }), _jsx("div", { ref: content, className: "hosty-panel-document", style: unsupported ? { display: "contents" } : undefined, inert: expanded && drawer, children: children }), _jsxs("nav", { "data-hosty-sdk": true, className: "hosty-panel-rail", "aria-label": "Tools", style: unsupported || !chrome.tools.enabled || chrome.tools.launcher === "hide" ? { display: "none" } : undefined, inert: unsupported || expanded && drawer, children: [tabs.map((panel, index) => _jsxs("button", { type: "button", title: `${panel.appName}: ${panel.label}`, "aria-label": panel.label, "aria-controls": bodyId, "aria-expanded": expanded && selectedKey === panelKey(panel.panel), onClick: () => void select(panel), onKeyDown: event => {
|
|
247
248
|
const delta = event.key === "ArrowDown" ? 1 : event.key === "ArrowUp" ? -1 : 0;
|
|
248
249
|
const next = event.key === "Home" ? 0 : event.key === "End" ? tabs.length - 1 : delta ? (index + delta + tabs.length) % tabs.length : null;
|
|
249
250
|
if (next !== null) {
|
|
250
251
|
event.preventDefault();
|
|
251
252
|
event.currentTarget.parentElement?.children[next]?.focus();
|
|
252
253
|
}
|
|
253
|
-
}, children: [renderIcon?.(panel) ?? panel.label.slice(0, 2), workspaceTools.some(t => t.key === panelKey(panel.panel) && t.attention > 0) ? _jsx("span", { className: "hosty-panel-attention", "aria-label": "Needs attention", children: "
|
|
254
|
+
}, children: [renderIcon?.(panel) ?? panel.label.slice(0, 2), workspaceTools.some(t => t.key === panelKey(panel.panel) && t.attention > 0) ? _jsx("span", { className: "hosty-panel-attention", "aria-label": "Needs attention", children: _jsx(SdkIcon, { name: "warning" }) }) : null] }, panelKey(panel.panel))), _jsx("button", { type: "button", "aria-label": "Add tool", title: "Add tool", onClick: () => { setError(null); setChooser(true); void host?.refresh(); }, children: _jsx(SdkIcon, { name: "plus" }) })] }), expanded && drawer && _jsx("button", { type: "button", className: "hosty-panel-backdrop", "aria-label": "Return to document", onClick: () => host?.setExpanded(false, workspaceId) }), _jsxs("aside", { "data-hosty-sdk": true, ref: body, id: bodyId, className: "hosty-panel-body", hidden: !expanded, inert: !expanded, role: drawer ? "dialog" : "complementary", "aria-modal": drawer && expanded ? true : undefined, "aria-label": selected?.label ?? "Tool", onKeyDown: event => {
|
|
254
255
|
if (!drawer)
|
|
255
256
|
return;
|
|
256
257
|
if (event.key === "Escape") {
|
|
@@ -279,24 +280,27 @@ export function HostyPanelWorkspace({ children, workspaceId = DEFAULT_WORKSPACE,
|
|
|
279
280
|
event.preventDefault();
|
|
280
281
|
host?.setWidth(width + (event.key === "ArrowLeft" ? 16 : -16), workspaceId);
|
|
281
282
|
}
|
|
282
|
-
} }), _jsxs("header", { children: [_jsx("strong", { children: selected?.label ?? "Tool" }), tool?.resolved.standaloneHref && _jsx("a", { href: tool.resolved.standaloneHref, target: "_blank", rel: "noopener noreferrer", title: "Open separately", "aria-label": "Open separately", children: "
|
|
283
|
+
} }), _jsxs("header", { children: [_jsx("strong", { children: selected?.label ?? "Tool" }), tool?.resolved.standaloneHref && _jsx("a", { href: tool.resolved.standaloneHref, target: "_blank", rel: "noopener noreferrer", title: "Open separately", "aria-label": "Open separately", children: _jsx(SdkIcon, { name: "external" }) }), (drawer || minimizeControl === "always") && _jsx("button", { ref: minimize, type: "button", onClick: () => host?.setExpanded(false, workspaceId), "aria-label": "Minimize panel", title: "Minimize panel (keep tool open)", children: _jsx(SdkIcon, { name: "minus" }) })] }), tool?.resolved.state === "degraded" && _jsx("div", { className: "hosty-panel-state", role: "status", children: "This app\u2019s health check is failing. Its page may still work." }), tool?.readiness === "unreported" && _jsxs("div", { className: "hosty-panel-state", role: "status", children: ["This tool has not confirmed that it loaded. If the page is blank or refuses to connect, use ", _jsx("a", { href: tool.resolved.standaloneHref ?? undefined, target: "_blank", rel: "noopener noreferrer", children: "Open separately" }), "."] }), tool?.readiness === "auth-required" && _jsx("div", { className: "hosty-panel-state", role: "status", children: "Check access in the tool below, then retry opening the discussion." }), tool && workspaceTools.filter(t => t.key === tool.key).length > 1 && _jsxs("label", { className: "hosty-panel-state", children: ["Open views", _jsx("select", { "aria-label": "Tool instance", value: tool.presentationId, onChange: event => host?.focusPresentation(event.target.value), children: workspaceTools.filter(t => t.key === tool.key).map((item, index) => _jsxs("option", { value: item.presentationId, children: [selected?.label ?? "Tool", " ", index + 1] }, item.presentationId)) })] }), _jsx("div", { ref: viewport, className: "hosty-panel-viewport", "aria-owns": tool ? `hosty-presentation-${tool.presentationId}` : undefined }), drawer && expanded && _jsx("span", { tabIndex: 0, "aria-hidden": "true", className: "hosty-panel-focus-guard", onFocus: () => minimize.current?.focus() })] }), _jsxs("dialog", { "data-hosty-sdk": true, ref: chooserRef, className: "hosty-tool-chooser", onCancel: () => setChooser(false), "aria-label": "Add tool", children: [_jsxs("header", { children: [_jsx("strong", { children: "Add tool" }), _jsx("button", { type: "button", onClick: () => setChooser(false), "aria-label": "Close tool chooser", children: _jsx(SdkIcon, { name: "close" }) })] }), _jsx("p", { children: "Open a tool beside your document." }), error && _jsx("p", { role: "status", children: error }), state.error && _jsx("p", { role: "status", children: state.error.message }), !state.bootstrap?.canHost && _jsx("p", { children: "This app needs optional panel access in Hosty Core to show tools here. You can still use the app." }), state.bootstrap?.canHost && !state.catalog.length && _jsx("p", { children: "No tools are available for your account." }), _jsx("ul", { children: state.catalog.map(panel => _jsxs("li", { children: [_jsxs("button", { type: "button", onClick: () => void select(panel), children: [_jsx("strong", { children: panel.label }), _jsxs("span", { children: [panel.appName, panel.available ? "" : ` · ${panel.state}`] })] }), !panel.available && panel.canStart && onStartApp && _jsx("button", { type: "button", onClick: () => void Promise.resolve(onStartApp(panel.panel.appId)).then(() => host?.refresh()), children: "Start app" })] }, panelKey(panel.panel))) }), _jsx("button", { type: "button", onClick: () => void host?.refresh(), children: "Refresh tools" })] }), error && !chooser && _jsxs("div", { "data-hosty-sdk": true, className: "hosty-panel-error", role: "status", children: [error, " ", _jsx("button", { type: "button", onClick: () => { setChooser(true); setError(null); }, children: "Choose tool" })] })] });
|
|
283
284
|
}
|
|
284
285
|
const styles = `
|
|
285
286
|
.hosty-panel-viewport{flex:1;min-height:0;min-width:0}
|
|
286
287
|
.hosty-app-layout{display:flex;width:100%;height:100%;min-width:0;min-height:0}.hosty-app-content{flex:1;min-width:0;min-height:0;overflow:auto}
|
|
287
|
-
|
|
288
|
-
.hosty-panel-
|
|
289
|
-
|
|
290
|
-
.hosty-panel-
|
|
291
|
-
.hosty-panel-
|
|
292
|
-
.hosty-panel-rail button:hover,.hosty-panel-
|
|
293
|
-
.hosty-panel-
|
|
294
|
-
.hosty-panel-
|
|
295
|
-
.hosty-panel-body
|
|
288
|
+
.hosty-panel-workspace{display:flex;position:relative;min-width:0;min-height:0;width:100%;height:100dvh;overflow:hidden}
|
|
289
|
+
:is(.hosty-panel-rail,.hosty-panel-body,.hosty-tool-chooser,.hosty-panel-error){--hp-bg:var(--hosty-panel-background,var(--hs-bg));--hp-fg:var(--hosty-panel-foreground,var(--hs-fg));--hp-muted:var(--hosty-panel-muted,var(--hs-muted));--hp-border:var(--hosty-panel-border,var(--hs-border));background:var(--hp-bg);color:var(--hp-fg)}
|
|
290
|
+
.hosty-panel-document{flex:1;min-width:0;min-height:0;overflow:auto}
|
|
291
|
+
.hosty-panel-rail{order:3;flex:0 0 48px;display:flex;flex-direction:column;align-items:center;gap:8px;padding:10px 0;border-left:1px solid var(--hp-border);overflow:auto}
|
|
292
|
+
.hosty-panel-rail button,.hosty-panel-body header button,.hosty-panel-body header a,.hosty-tool-chooser header button{position:relative;display:grid;place-items:center;flex-shrink:0;border:1px solid transparent;border-radius:8px;width:32px;height:32px;padding:0;background:transparent;color:inherit;font:500 13px var(--hs-font);cursor:pointer;text-decoration:none;transition:background-color 120ms ease-out,color 120ms ease-out}
|
|
293
|
+
.hosty-panel-rail button:hover,.hosty-panel-body header :is(button,a):hover,.hosty-tool-chooser header button:hover{background:var(--hs-brand-soft);color:var(--hs-fg)}
|
|
294
|
+
.hosty-panel-rail button[aria-expanded=true]{background:var(--hs-brand);color:var(--hs-brand-fg)}
|
|
295
|
+
.hosty-panel-attention{position:absolute;top:-2px;right:-2px;display:block;background:var(--hp-bg);border-radius:50%;color:var(--hs-warning)}.hosty-panel-attention>.hosty-sdk-icon{width:12px;height:12px}
|
|
296
|
+
.hosty-panel-body{order:2;display:flex;flex:0 0 var(--hosty-panel-width);flex-direction:column;position:relative;min-width:0;min-height:0;margin-left:12px;border-left:1px solid var(--hp-border)}
|
|
297
|
+
.hosty-panel-body[hidden],.hosty-panel-frame[hidden]{display:none}
|
|
298
|
+
.hosty-panel-body header,.hosty-tool-chooser header{display:flex;align-items:center;gap:6px;min-height:44px;padding:0 10px;border-bottom:1px solid var(--hp-border);font:500 14px/20px var(--hs-font)}.hosty-panel-body header strong,.hosty-tool-chooser header strong{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
|
296
299
|
.hosty-panel-focus-guard{position:absolute;width:1px;height:1px;overflow:hidden;clip-path:inset(50%)}
|
|
297
|
-
.hosty-panel-frame{position:relative;flex:1;min-height:0;overflow:hidden}.hosty-panel-frame iframe{display:block}
|
|
298
|
-
.hosty-panel-
|
|
299
|
-
.hosty-panel-
|
|
300
|
-
.hosty-panel-
|
|
301
|
-
.hosty-
|
|
300
|
+
.hosty-panel-frame{position:relative;flex:1;min-height:0;overflow:hidden}.hosty-panel-frame iframe{display:block}
|
|
301
|
+
.hosty-panel-resize{position:absolute;left:-12px;top:0;bottom:0;width:12px;cursor:col-resize;touch-action:none}.hosty-panel-resize:hover{background:var(--hs-brand-soft)}
|
|
302
|
+
.hosty-panel-state{padding:8px 12px;font:400 13px/20px var(--hs-font);border-bottom:1px solid var(--hp-border);color:var(--hp-muted)}.hosty-panel-state a{color:var(--hs-info);text-decoration:underline;text-underline-offset:3px}.hosty-panel-state select{display:block;max-width:100%;margin-top:6px;padding:6px 10px;border:1px solid var(--hp-border);border-radius:8px;background:var(--hp-bg);color:var(--hp-fg)}
|
|
303
|
+
.hosty-panel-workspace[data-hosty-panel-layout=drawer] .hosty-panel-body{position:absolute;inset:0 0 0 auto;width:min(440px,100%);z-index:102;margin:0;box-shadow:-8px 0 30px #0F1B2D29}.hosty-panel-workspace[data-hosty-panel-layout=drawer] .hosty-panel-resize{display:none}
|
|
304
|
+
.hosty-panel-backdrop{position:absolute;inset:0;border:0;background:#0F1B2D57;z-index:101;cursor:pointer}.hosty-panel-backdrop:focus-visible{outline:2px solid #A3E635;outline-offset:-2px}
|
|
305
|
+
.hosty-tool-chooser{width:min(440px,calc(100vw - 32px));max-height:calc(100dvh - 32px);margin:auto;border:1px solid var(--hp-border);border-radius:14px;padding:20px;background:var(--hp-bg);color:var(--hp-fg);overflow:auto;box-shadow:0 14px 34px -8px #0F1B2D3D}.hosty-tool-chooser::backdrop{background:#0F1B2D57}.hosty-tool-chooser header{border:0;padding:0;min-height:32px}.hosty-tool-chooser>p{margin:12px 0;color:var(--hp-muted);font-size:13px}.hosty-tool-chooser ul{list-style:none;padding:0;margin:16px 0;display:grid;gap:8px}.hosty-tool-chooser li{display:flex;gap:8px}.hosty-tool-chooser li>button,.hosty-tool-chooser>button,.hosty-panel-error>button{font:500 14px/20px var(--hs-font);border:1px solid var(--hp-border);background:var(--hs-card);color:inherit;border-radius:8px;padding:8px 12px;cursor:pointer}.hosty-tool-chooser li>button{flex:1;text-align:left;padding:12px}.hosty-tool-chooser li>button:hover,.hosty-tool-chooser>button:hover,.hosty-panel-error>button:hover{background:var(--hs-brand-soft)}.hosty-tool-chooser li span{display:block;color:var(--hp-muted);font:400 13px/20px var(--hs-font)}.hosty-panel-error{position:absolute;bottom:16px;right:60px;max-width:calc(100% - 76px);z-index:4;border:1px solid var(--hp-border);border-radius:10px;padding:12px;font:400 13px/20px var(--hs-font);box-shadow:0 4px 14px -4px #0F1B2D29}.hosty-panel-error>button{margin:8px 0 0 8px}
|
|
302
306
|
`;
|
package/dist/ui-types.d.ts
CHANGED
|
@@ -165,10 +165,17 @@ export type UiResolvedNavigation = {
|
|
|
165
165
|
state: string;
|
|
166
166
|
standaloneHref: string | null;
|
|
167
167
|
};
|
|
168
|
+
export declare const UI_MAIN_NAVIGATION_CAPABILITY = "ui.main-navigation.v1";
|
|
168
169
|
export type UiActionOptions = {
|
|
169
170
|
requestId: string;
|
|
170
171
|
signal?: AbortSignal;
|
|
171
172
|
};
|
|
173
|
+
/** Main placement is explicit; only the resolved navigation entry's query can be overridden. */
|
|
174
|
+
export type UiNavigationOptions = UiActionOptions & {
|
|
175
|
+
placement?: "main";
|
|
176
|
+
search?: string;
|
|
177
|
+
};
|
|
178
|
+
export declare function navigationSearch(value: unknown): value is string | undefined;
|
|
172
179
|
export type UiActionResult = {
|
|
173
180
|
status: "accepted";
|
|
174
181
|
requestId: string;
|
package/dist/ui-types.js
CHANGED
|
@@ -42,3 +42,8 @@ export function buildSurfaceOpenUrl(coreOrigin, destination) {
|
|
|
42
42
|
return url.href;
|
|
43
43
|
}
|
|
44
44
|
export const UI_COMPOSITION_CAPABILITY = "ui.composition.v1";
|
|
45
|
+
export const UI_MAIN_NAVIGATION_CAPABILITY = "ui.main-navigation.v1";
|
|
46
|
+
export function navigationSearch(value) {
|
|
47
|
+
return value === undefined || typeof value === "string" && value.length <= 4096
|
|
48
|
+
&& (value === "" || value.startsWith("?")) && !/[#\u0000-\u001f\u007f]/.test(value);
|
|
49
|
+
}
|
package/dist/ui.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { UiChromeSnapshot, UiNavigationSnapshot, UiActionOptions, UiActionResult, PanelTarget, PresentationOptions, PresentationResult, UiEnvironment, UiEvent } from "@hosty-sdk/app/ui/types";
|
|
1
|
+
import type { UiChromeSnapshot, UiNavigationSnapshot, UiActionOptions, UiNavigationOptions, UiActionResult, PanelTarget, PresentationOptions, PresentationResult, UiEnvironment, UiEvent } from "@hosty-sdk/app/ui/types";
|
|
2
2
|
export * from "@hosty-sdk/app/ui/types";
|
|
3
3
|
export interface UiClient {
|
|
4
4
|
getEnvironment(): UiEnvironment;
|
|
@@ -6,7 +6,7 @@ export interface UiClient {
|
|
|
6
6
|
subscribeChrome(listener: () => void): () => void;
|
|
7
7
|
getNavigation(): UiNavigationSnapshot;
|
|
8
8
|
subscribeNavigation(listener: () => void): () => void;
|
|
9
|
-
navigate(entryKey: string, options:
|
|
9
|
+
navigate(entryKey: string, options: UiNavigationOptions): Promise<UiActionResult>;
|
|
10
10
|
openToolPicker(options: UiActionOptions): Promise<UiActionResult>;
|
|
11
11
|
openSurface(target: PanelTarget, options: PresentationOptions): Promise<PresentationResult>;
|
|
12
12
|
getPresentation(requestId: string, options?: {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hosty-sdk/app",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.34.1",
|
|
4
4
|
"description": "Hosty app auth: session state machine, recovery bridge, Core revalidation, and the embedder responder.",
|
|
5
5
|
"license": "AGPL-3.0-only",
|
|
6
6
|
"type": "module",
|
|
@@ -132,14 +132,14 @@
|
|
|
132
132
|
},
|
|
133
133
|
"scripts": {
|
|
134
134
|
"test": "vitest run",
|
|
135
|
-
"build": "tsc -p tsconfig.build.json && node scripts/fix-dist-extensions.mjs"
|
|
135
|
+
"build": "tsc -p tsconfig.build.json && node scripts/fix-dist-extensions.mjs && node scripts/copy-chrome-assets.mjs"
|
|
136
136
|
},
|
|
137
137
|
"dependencies": {
|
|
138
138
|
"@noble/hashes": "^2.4.0",
|
|
139
139
|
"server-only": "^0.0.1"
|
|
140
140
|
},
|
|
141
141
|
"peerDependencies": {
|
|
142
|
-
"next": "^16.
|
|
142
|
+
"next": "^16.4.0",
|
|
143
143
|
"react": "^19.2.8",
|
|
144
144
|
"react-dom": "^19.2.8"
|
|
145
145
|
},
|
|
@@ -155,14 +155,15 @@
|
|
|
155
155
|
}
|
|
156
156
|
},
|
|
157
157
|
"devDependencies": {
|
|
158
|
-
"@types/node": "^24.19.
|
|
158
|
+
"@types/node": "^24.19.2",
|
|
159
159
|
"@types/react": "^19.3.0",
|
|
160
160
|
"@types/react-dom": "^19.3.0",
|
|
161
|
-
"jsdom": "^30.1.
|
|
161
|
+
"jsdom": "^30.1.2",
|
|
162
162
|
"react": "^19.3.0",
|
|
163
163
|
"react-dom": "^19.3.0",
|
|
164
164
|
"typescript": "^6",
|
|
165
|
-
"vitest": "^5.0.3"
|
|
165
|
+
"vitest": "^5.0.3",
|
|
166
|
+
"vite": "^8.3.5"
|
|
166
167
|
},
|
|
167
168
|
"repository": {
|
|
168
169
|
"type": "git",
|