@gooddata/sdk-ui-pluggable-host 11.51.0-alpha.4 → 11.51.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/esm/components/HostUiContainer.d.ts.map +1 -1
- package/esm/components/HostUiContainer.js +22 -12
- package/esm/components/navigationGuard.d.ts +10 -0
- package/esm/components/navigationGuard.d.ts.map +1 -0
- package/esm/components/navigationGuard.js +25 -0
- package/esm/loader/routing.d.ts +2 -0
- package/esm/loader/routing.d.ts.map +1 -1
- package/esm/loader/routing.js +2 -2
- package/esm/ui/GenAIChat.d.ts.map +1 -1
- package/esm/ui/GenAIChat.js +9 -14
- package/esm/ui/HostChat.d.ts +1 -6
- package/esm/ui/HostChat.d.ts.map +1 -1
- package/esm/ui/HostChat.js +1 -2
- package/esm/ui/HostChrome.d.ts.map +1 -1
- package/esm/ui/HostChrome.js +12 -3
- package/esm/ui/PluggableApplicationRenderer.d.ts +6 -6
- package/esm/ui/PluggableApplicationRenderer.d.ts.map +1 -1
- package/esm/ui/PluggableApplicationRenderer.js +32 -18
- package/esm/ui/appMenuItems.d.ts.map +1 -1
- package/esm/ui/appMenuItems.js +4 -10
- package/esm/ui/chromeHelpers.d.ts +9 -0
- package/esm/ui/chromeHelpers.d.ts.map +1 -1
- package/esm/ui/chromeHelpers.js +15 -0
- package/esm/ui/useHostChromeChat.d.ts +1 -4
- package/esm/ui/useHostChromeChat.d.ts.map +1 -1
- package/esm/ui/useHostChromeChat.js +2 -3
- package/package.json +17 -17
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"HostUiContainer.d.ts","sourceRoot":"","sources":["../../src/components/HostUiContainer.tsx"],"names":[],"mappings":"AAKA,OAAO,EAAE,KAAK,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAErD,OAAO,EAA0B,KAAK,gCAAgC,EAAE,MAAM,qBAAqB,CAAC;AACpG,OAAO,
|
|
1
|
+
{"version":3,"file":"HostUiContainer.d.ts","sourceRoot":"","sources":["../../src/components/HostUiContainer.tsx"],"names":[],"mappings":"AAKA,OAAO,EAAE,KAAK,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAErD,OAAO,EAA0B,KAAK,gCAAgC,EAAE,MAAM,qBAAqB,CAAC;AACpG,OAAO,EAGH,KAAK,gBAAgB,EAExB,MAAM,2CAA2C,CAAC;AAiBnD,OAAO,wBAAwB,CAAC;AAGhC,MAAM,WAAW,qBAAqB;IAClC,GAAG,EAAE,gBAAgB,CAAC;IACtB,IAAI,EAAE,gCAAgC,EAAE,CAAC;IACzC,QAAQ,EAAE,MAAM,CAAC;IACjB,cAAc,EAAE,gBAAgB,CAAC;CACpC;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,EAAE,GAAG,EAAE,IAAI,EAAE,QAAQ,EAAE,cAAc,EAAE,EAAE,qBAAqB,2CA+S7F"}
|
|
@@ -10,8 +10,9 @@ import { getActiveInternalApplication } from "../loader/routing.js";
|
|
|
10
10
|
import { HostChat, } from "../ui/HostChat.js";
|
|
11
11
|
import { HostIntlProvider } from "../ui/HostIntlProvider.js";
|
|
12
12
|
import { PluggableApplicationRenderer } from "../ui/PluggableApplicationRenderer.js";
|
|
13
|
-
import "./HostUiContainer.scss";
|
|
14
13
|
import { resolveHostUiModule } from "../ui/resolveHostUiModule.js";
|
|
14
|
+
import "./HostUiContainer.scss";
|
|
15
|
+
import { runGuardedNavigation } from "./navigationGuard.js";
|
|
15
16
|
/**
|
|
16
17
|
* Mounts the host UI module into a container div, then renders the active
|
|
17
18
|
* pluggable application into the host's app slot. Handles all lifecycle
|
|
@@ -63,10 +64,7 @@ export function HostUiContainer({ ctx, apps, pathname, routerNavigate }) {
|
|
|
63
64
|
// app mount handle) and read by HostChat's chat so embedded apps can handle link clicks in-app.
|
|
64
65
|
const appAiLinkClickRef = useRef(undefined);
|
|
65
66
|
const onAppLinkClick = useCallback((link) => appAiLinkClickRef.current?.(link) ?? false, []);
|
|
66
|
-
|
|
67
|
-
// app mount handle) and read by HostChat's chat so embedded apps can handle events in-app.
|
|
68
|
-
const appEventReceiveRef = useRef(undefined);
|
|
69
|
-
const onAppEventReceive = useCallback((event) => appEventReceiveRef.current?.(event), []);
|
|
67
|
+
const appNavigationRequestRef = useRef(undefined);
|
|
70
68
|
const activeAppRef = useAutoupdateRef(activeInternalApplication);
|
|
71
69
|
const onHeaderChange = useCallback((appId, header) => {
|
|
72
70
|
if (activeAppRef.current?.id === appId) {
|
|
@@ -78,11 +76,19 @@ export function HostUiContainer({ ctx, apps, pathname, routerNavigate }) {
|
|
|
78
76
|
setPageTitle(title);
|
|
79
77
|
}
|
|
80
78
|
}, [activeAppRef]);
|
|
81
|
-
// Stable navigation callbacks that always use the latest router navigate
|
|
79
|
+
// Stable navigation callbacks that always use the latest router navigate.
|
|
80
|
+
// Both must stay stable: the host UI module captures them once at mount, so a changing dependency
|
|
81
|
+
// would leave it calling a stale closure.
|
|
82
82
|
const navigateRef = useAutoupdateRef(routerNavigate);
|
|
83
|
+
// `replace` stays unguarded: it serves programmatic redirects, not user intent.
|
|
83
84
|
const navigate = useCallback((url) => {
|
|
84
|
-
|
|
85
|
-
|
|
85
|
+
runGuardedNavigation({
|
|
86
|
+
url,
|
|
87
|
+
currentPathname: latestMountStateRef.current.pathname,
|
|
88
|
+
guardRef: appNavigationRequestRef,
|
|
89
|
+
navigate: (target) => void navigateRef.current(target),
|
|
90
|
+
});
|
|
91
|
+
}, [navigateRef, latestMountStateRef]);
|
|
86
92
|
const replace = useCallback((url) => {
|
|
87
93
|
void navigateRef.current(url, { replace: true });
|
|
88
94
|
}, [navigateRef]);
|
|
@@ -134,8 +140,12 @@ export function HostUiContainer({ ctx, apps, pathname, routerNavigate }) {
|
|
|
134
140
|
appRootRef.current = undefined;
|
|
135
141
|
const handle = handleRef.current;
|
|
136
142
|
handleRef.current = undefined;
|
|
137
|
-
|
|
138
|
-
|
|
143
|
+
// This cleanup runs synchronously inside the commit of the root that renders this
|
|
144
|
+
// component, and unmount() is synchronous — doing it here re-enters React mid-render.
|
|
145
|
+
queueMicrotask(() => {
|
|
146
|
+
appRoot?.unmount();
|
|
147
|
+
handle?.unmount();
|
|
148
|
+
});
|
|
139
149
|
};
|
|
140
150
|
// Mount only once; updates are pushed via handle
|
|
141
151
|
}, [latestMountStateRef, navigationMountRef]);
|
|
@@ -204,7 +214,7 @@ export function HostUiContainer({ ctx, apps, pathname, routerNavigate }) {
|
|
|
204
214
|
return;
|
|
205
215
|
}
|
|
206
216
|
if (activeInternalApplication) {
|
|
207
|
-
appRootRef.current.render(_jsx(HostIntlProvider, { locale: resolveLocale(ctx.preferredLocale), children: _jsx(PluggableApplicationRenderer, { app: activeInternalApplication, ctx: ctx, pathname: pathname, aiAssistantOpen: aiAssistantOpen, onOpenAiAssistant: requestOpenAi, onCloseAiAssistant: requestCloseAi, onAiAssistantContext: setAiAssistantContext, aiLinkClickHandlerRef: appAiLinkClickRef,
|
|
217
|
+
appRootRef.current.render(_jsx(HostIntlProvider, { locale: resolveLocale(ctx.preferredLocale), children: _jsx(PluggableApplicationRenderer, { app: activeInternalApplication, ctx: ctx, pathname: pathname, aiAssistantOpen: aiAssistantOpen, onOpenAiAssistant: requestOpenAi, onCloseAiAssistant: requestCloseAi, onAiAssistantContext: setAiAssistantContext, aiLinkClickHandlerRef: appAiLinkClickRef, navigationRequestRef: appNavigationRequestRef, onHeaderChange: onHeaderChange, onDocumentTitleChange: onDocumentTitleChange }, activeInternalApplication.id) }));
|
|
208
218
|
}
|
|
209
219
|
else {
|
|
210
220
|
appRootRef.current.render(null);
|
|
@@ -222,5 +232,5 @@ export function HostUiContainer({ ctx, apps, pathname, routerNavigate }) {
|
|
|
222
232
|
setAiAssistantContext,
|
|
223
233
|
]);
|
|
224
234
|
return (_jsxs(_Fragment, { children: [
|
|
225
|
-
_jsx("div", { ref: containerRef, className: "gd-host-ui-container" }), hostReady && !ctx.isExportMode ? (_jsx(HostChat, { ctx: ctx, resolvedApplications: apps, pathname: pathname, activeAppId: activeInternalApplication?.id, visibility: aiVisibility, context: aiContext, onOpenChange: setAiAssistantOpen, onChatStateChange: onChatStateChange, onAppLinkClick: onAppLinkClick
|
|
235
|
+
_jsx("div", { ref: containerRef, className: "gd-host-ui-container" }), hostReady && !ctx.isExportMode ? (_jsx(HostChat, { ctx: ctx, resolvedApplications: apps, pathname: pathname, activeAppId: activeInternalApplication?.id, visibility: aiVisibility, context: aiContext, onOpenChange: setAiAssistantOpen, onChatStateChange: onChatStateChange, onAppLinkClick: onAppLinkClick })) : null] }));
|
|
226
236
|
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { type RefObject } from "react";
|
|
2
|
+
import { type IHostNavigationRequest } from "@gooddata/sdk-pluggable-application-model";
|
|
3
|
+
export declare function isSameDestination(targetUrl: string, currentPathname: string): boolean;
|
|
4
|
+
export declare function runGuardedNavigation(options: {
|
|
5
|
+
url: string;
|
|
6
|
+
currentPathname: string;
|
|
7
|
+
guardRef: RefObject<((request: IHostNavigationRequest) => boolean) | undefined>;
|
|
8
|
+
navigate: (url: string) => void;
|
|
9
|
+
}): void;
|
|
10
|
+
//# sourceMappingURL=navigationGuard.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"navigationGuard.d.ts","sourceRoot":"","sources":["../../src/components/navigationGuard.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAEvC,OAAO,EAAE,KAAK,sBAAsB,EAAE,MAAM,2CAA2C,CAAC;AAKxF,wBAAgB,iBAAiB,CAAC,SAAS,EAAE,MAAM,EAAE,eAAe,EAAE,MAAM,GAAG,OAAO,CAKrF;AAED,wBAAgB,oBAAoB,CAAC,OAAO,EAAE;IAC1C,GAAG,EAAE,MAAM,CAAC;IACZ,eAAe,EAAE,MAAM,CAAC;IACxB,QAAQ,EAAE,SAAS,CAAC,CAAC,CAAC,OAAO,EAAE,sBAAsB,KAAK,OAAO,CAAC,GAAG,SAAS,CAAC,CAAC;IAChF,QAAQ,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;CACnC,GAAG,IAAI,CAmBP"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
// (C) 2026 GoodData Corporation
|
|
2
|
+
import { normalizePath, stripEmbedPrefix } from "../loader/routing.js";
|
|
3
|
+
// Pathname is the most the host can compare: it cannot see the module's in-app route.
|
|
4
|
+
export function isSameDestination(targetUrl, currentPathname) {
|
|
5
|
+
const targetPathname = targetUrl.split(/[?#]/)[0] ?? "";
|
|
6
|
+
return (normalizePath(stripEmbedPrefix(targetPathname)) === normalizePath(stripEmbedPrefix(currentPathname)));
|
|
7
|
+
}
|
|
8
|
+
export function runGuardedNavigation(options) {
|
|
9
|
+
const { url, currentPathname, guardRef, navigate } = options;
|
|
10
|
+
const guard = guardRef.current;
|
|
11
|
+
let consumed = false;
|
|
12
|
+
const proceed = () => {
|
|
13
|
+
// An application can retain `proceed`: a second call would push a duplicate history entry, and
|
|
14
|
+
// a call surviving into another mount would navigate on its behalf.
|
|
15
|
+
if (consumed || guardRef.current !== guard) {
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
consumed = true;
|
|
19
|
+
navigate(url);
|
|
20
|
+
};
|
|
21
|
+
if (!isSameDestination(url, currentPathname) && guard?.({ url, proceed })) {
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
proceed();
|
|
25
|
+
}
|
package/esm/loader/routing.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { type ApplicationScope, type PluggableApplicationRegistryItem } from "@gooddata/sdk-model";
|
|
2
2
|
import { type IPlatformContext } from "@gooddata/sdk-pluggable-application-model";
|
|
3
|
+
export declare function stripEmbedPrefix(pathname: string): string;
|
|
3
4
|
/**
|
|
4
5
|
* Returns the application scope for the given URL path, or undefined if the path
|
|
5
6
|
* does not match a known scope.
|
|
@@ -10,6 +11,7 @@ export declare function getApplicationScopeFromPath(path: string): ApplicationSc
|
|
|
10
11
|
* does not contain a workspace id.
|
|
11
12
|
*/
|
|
12
13
|
export declare function getWorkspaceIdFromPath(pathname: string | undefined): string | undefined;
|
|
14
|
+
export declare function normalizePath(path: string): string;
|
|
13
15
|
/**
|
|
14
16
|
* Substitutes `{workspaceId}` placeholders in an external app URL with the
|
|
15
17
|
* currently resolved workspace id. URLs with no placeholder pass through
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"routing.d.ts","sourceRoot":"","sources":["../../src/loader/routing.ts"],"names":[],"mappings":"AAEA,OAAO,EACH,KAAK,gBAAgB,EACrB,KAAK,gCAAgC,EAIxC,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,KAAK,gBAAgB,EAAE,MAAM,2CAA2C,CAAC;
|
|
1
|
+
{"version":3,"file":"routing.d.ts","sourceRoot":"","sources":["../../src/loader/routing.ts"],"names":[],"mappings":"AAEA,OAAO,EACH,KAAK,gBAAgB,EACrB,KAAK,gCAAgC,EAIxC,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,KAAK,gBAAgB,EAAE,MAAM,2CAA2C,CAAC;AAIlF,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAEzD;AAED;;;GAGG;AACH,wBAAgB,2BAA2B,CAAC,IAAI,EAAE,MAAM,GAAG,gBAAgB,GAAG,SAAS,CAYtF;AAED;;;GAGG;AACH,wBAAgB,sBAAsB,CAAC,QAAQ,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,CAEvF;AAMD,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAMlD;AAgCD;;;;;GAKG;AACH,wBAAgB,iCAAiC,CAC7C,GAAG,EAAE,MAAM,EACX,GAAG,EAAE,gBAAgB,EACrB,QAAQ,CAAC,EAAE,MAAM,GAClB,MAAM,CAMR;AAED,wBAAgB,kBAAkB,CAC9B,GAAG,EAAE,gCAAgC,EACrC,GAAG,EAAE,gBAAgB,EACrB,QAAQ,CAAC,EAAE,MAAM,GAClB,MAAM,CAWR;AAED,wBAAgB,wBAAwB,CACpC,GAAG,EAAE,gCAAgC,EACrC,GAAG,EAAE,gBAAgB,EACrB,QAAQ,EAAE,MAAM,GACjB,OAAO,CAQT;AAED,wBAAgB,4BAA4B,CACxC,IAAI,EAAE,gCAAgC,EAAE,EACxC,GAAG,EAAE,gBAAgB,EACrB,QAAQ,EAAE,MAAM,GACjB,gCAAgC,GAAG,SAAS,CAK9C"}
|
package/esm/loader/routing.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// (C) 2026 GoodData Corporation
|
|
2
2
|
import { isExternalPluggableApplicationRegistryItem, isLocalPluggableApplicationRegistryItem, isRemotePluggableApplicationRegistryItem, } from "@gooddata/sdk-model";
|
|
3
3
|
const WORKSPACE_PATH_PATTERN = /^\/workspace\/(?<workspaceId>[^/]+)(?:\/|$)/;
|
|
4
|
-
function stripEmbedPrefix(pathname) {
|
|
4
|
+
export function stripEmbedPrefix(pathname) {
|
|
5
5
|
return pathname.startsWith("/embedded/") ? pathname.slice("/embedded".length) : pathname;
|
|
6
6
|
}
|
|
7
7
|
/**
|
|
@@ -29,7 +29,7 @@ export function getWorkspaceIdFromPath(pathname) {
|
|
|
29
29
|
function ensureLeadingSlash(path) {
|
|
30
30
|
return path.startsWith("/") ? path : `/${path}`;
|
|
31
31
|
}
|
|
32
|
-
function normalizePath(path) {
|
|
32
|
+
export function normalizePath(path) {
|
|
33
33
|
const prefixed = ensureLeadingSlash(path);
|
|
34
34
|
if (prefixed === "/") {
|
|
35
35
|
return prefixed;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"GenAIChat.d.ts","sourceRoot":"","sources":["../../src/ui/GenAIChat.tsx"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"GenAIChat.d.ts","sourceRoot":"","sources":["../../src/ui/GenAIChat.tsx"],"names":[],"mappings":"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"}
|
package/esm/ui/GenAIChat.js
CHANGED
|
@@ -26,20 +26,15 @@ export function GenAIChat({ workspaceId, open, onOpen, onClose, askedQuestion, a
|
|
|
26
26
|
// type (only visualizations did anything). Non-embedded mode uses the connected wrapper's default
|
|
27
27
|
// open/navigate handler instead.
|
|
28
28
|
const onLinkClick = useCallback((link) => {
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
else if (!areUrlsEqual) {
|
|
36
|
-
window.location.assign(link.itemUrl);
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
onAppLinkClick?.(link);
|
|
29
|
+
onAppLinkClick?.({
|
|
30
|
+
type: link.type,
|
|
31
|
+
id: link.id,
|
|
32
|
+
itemUrl: link.itemUrl,
|
|
33
|
+
newTab: link.newTab,
|
|
34
|
+
});
|
|
40
35
|
link.preventDefault();
|
|
41
|
-
return
|
|
42
|
-
}, [onAppLinkClick
|
|
36
|
+
return undefined;
|
|
37
|
+
}, [onAppLinkClick]);
|
|
43
38
|
const handleEvent = useCallback((event) => {
|
|
44
39
|
switch (event.name) {
|
|
45
40
|
case "opened":
|
|
@@ -84,5 +79,5 @@ export function GenAIChat({ workspaceId, open, onOpen, onClose, askedQuestion, a
|
|
|
84
79
|
break;
|
|
85
80
|
}
|
|
86
81
|
}, [addError, addSuccess, intl, onEvent]);
|
|
87
|
-
return (_jsx(GenAIChatDialogConnected, { backend: backend, workspace: workspaceId, locale: intl.locale, isOpen: open, onOpen: onOpen, onClose: onClose, askedQuestion: askedQuestion, askSeq: askSeq, userContext: userContext, appendToChat: appendToChat, agentId: agentId, replaceUserContext: replaceUserContext, ambientUserContext: ambientUserContext, includeTags: includeTags, excludeTags: excludeTags, canManage: canManageProject, canAnalyze: canAnalyzeProject, canFullControl: canFullControl, settings: settings, dialogPosition: dialogPosition, className: embedded ? EMBEDDED_CHAT_CLASS : undefined,
|
|
82
|
+
return (_jsx(GenAIChatDialogConnected, { backend: backend, workspace: workspaceId, locale: intl.locale, isOpen: open, onOpen: onOpen, onClose: onClose, askedQuestion: askedQuestion, askSeq: askSeq, userContext: userContext, appendToChat: appendToChat, agentId: agentId, replaceUserContext: replaceUserContext, ambientUserContext: ambientUserContext, includeTags: includeTags, excludeTags: excludeTags, canManage: canManageProject, canAnalyze: canAnalyzeProject, canFullControl: canFullControl, settings: settings, dialogPosition: dialogPosition, allowNativeLinks: embedded ? false : undefined, className: embedded ? EMBEDDED_CHAT_CLASS : undefined, onLinkClick: embedded ? onLinkClick : undefined, onEvent: handleEvent, returnFocusTo: embedded ? EMBEDDED_AI_TRIGGER_ID : HEADER_CHAT_BUTTON_ID }));
|
|
88
83
|
}
|
package/esm/ui/HostChat.d.ts
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { type IGenAIUserContext, type PluggableApplicationRegistryItem } from "@gooddata/sdk-model";
|
|
2
2
|
import { type IPlatformContext } from "@gooddata/sdk-pluggable-application-model";
|
|
3
|
-
import { GenAIChatEvent } from "./GenAIChat.js";
|
|
4
3
|
import "@gooddata/sdk-ui-gen-ai/styles/css/main.css";
|
|
5
4
|
/**
|
|
6
5
|
* AI-assistant open/close/toggle request. `seq` changes on every request so an identical repeat
|
|
@@ -59,10 +58,6 @@ export interface IHostChatProps {
|
|
|
59
58
|
* visualization as an in-place overlay). Returns true if the app handled it.
|
|
60
59
|
*/
|
|
61
60
|
onAppLinkClick?: (link: IHostChatLink) => boolean;
|
|
62
|
-
/**
|
|
63
|
-
* Delegates a chat event receive to the active application.
|
|
64
|
-
*/
|
|
65
|
-
onAppEventReceive?: (event: GenAIChatEvent) => void;
|
|
66
61
|
}
|
|
67
62
|
/**
|
|
68
63
|
* Owns and renders the host's single GenAI chat, decoupled from the host UI module.
|
|
@@ -73,5 +68,5 @@ export interface IHostChatProps {
|
|
|
73
68
|
* (embedded/export). The header chat button and app-side controls drive it through the `visibility`
|
|
74
69
|
* and `context` props; it reports its open-state and button state back via callbacks.
|
|
75
70
|
*/
|
|
76
|
-
export declare function HostChat({ ctx, resolvedApplications, pathname, activeAppId, visibility, context, onOpenChange, onChatStateChange, onAppLinkClick
|
|
71
|
+
export declare function HostChat({ ctx, resolvedApplications, pathname, activeAppId, visibility, context, onOpenChange, onChatStateChange, onAppLinkClick }: IHostChatProps): import("react/jsx-runtime").JSX.Element;
|
|
77
72
|
//# sourceMappingURL=HostChat.d.ts.map
|
package/esm/ui/HostChat.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"HostChat.d.ts","sourceRoot":"","sources":["../../src/ui/HostChat.tsx"],"names":[],"mappings":"AAIA,OAAO,EAAE,KAAK,iBAAiB,EAAE,KAAK,gCAAgC,EAAE,MAAM,qBAAqB,CAAC;AACpG,OAAO,EAAE,KAAK,gBAAgB,EAAE,MAAM,2CAA2C,CAAC;
|
|
1
|
+
{"version":3,"file":"HostChat.d.ts","sourceRoot":"","sources":["../../src/ui/HostChat.tsx"],"names":[],"mappings":"AAIA,OAAO,EAAE,KAAK,iBAAiB,EAAE,KAAK,gCAAgC,EAAE,MAAM,qBAAqB,CAAC;AACpG,OAAO,EAAE,KAAK,gBAAgB,EAAE,MAAM,2CAA2C,CAAC;AAUlF,OAAO,6CAA6C,CAAC;AAErD;;;GAGG;AACH,MAAM,WAAW,mBAAmB;IAChC,IAAI,EAAE,MAAM,GAAG,OAAO,GAAG,QAAQ,CAAC;IAClC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,iBAAiB,CAAC;IAChC,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,GAAG,EAAE,MAAM,CAAC;CACf;AAED,+FAA+F;AAC/F,MAAM,WAAW,aAAa;IAC1B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,OAAO,CAAC;CACpB;AAED;;;GAGG;AACH,MAAM,WAAW,gBAAgB;IAC7B,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;IACvB,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;IACvB,cAAc,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;IAClC,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB;;;OAGG;IACH,WAAW,CAAC,EAAE,iBAAiB,CAAC;CACnC;AAED,MAAM,WAAW,cAAc;IAC3B,GAAG,EAAE,gBAAgB,CAAC;IACtB,oBAAoB,EAAE,gCAAgC,EAAE,CAAC;IACzD,QAAQ,EAAE,MAAM,CAAC;IACjB,qFAAqF;IACrF,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,+FAA+F;IAC/F,UAAU,CAAC,EAAE,mBAAmB,GAAG,IAAI,CAAC;IACxC,oFAAoF;IACpF,OAAO,CAAC,EAAE,gBAAgB,GAAG,IAAI,CAAC;IAClC,mFAAmF;IACnF,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC;IACvC,6FAA6F;IAC7F,iBAAiB,CAAC,EAAE,CAAC,KAAK,EAAE;QAAE,YAAY,EAAE,OAAO,CAAC;QAAC,MAAM,EAAE,OAAO,CAAA;KAAE,KAAK,IAAI,CAAC;IAChF;;;OAGG;IACH,cAAc,CAAC,EAAE,CAAC,IAAI,EAAE,aAAa,KAAK,OAAO,CAAC;CACrD;AAED;;;;;;;;GAQG;AACH,wBAAgB,QAAQ,CAAC,EACrB,GAAG,EACH,oBAAoB,EACpB,QAAQ,EACR,WAAW,EACX,UAAiB,EACjB,OAAc,EACd,YAAY,EACZ,iBAAiB,EACjB,cAAc,EACjB,EAAE,cAAc,2CA0FhB"}
|
package/esm/ui/HostChat.js
CHANGED
|
@@ -18,7 +18,7 @@ import "@gooddata/sdk-ui-gen-ai/styles/css/main.css";
|
|
|
18
18
|
* (embedded/export). The header chat button and app-side controls drive it through the `visibility`
|
|
19
19
|
* and `context` props; it reports its open-state and button state back via callbacks.
|
|
20
20
|
*/
|
|
21
|
-
export function HostChat({ ctx, resolvedApplications, pathname, activeAppId, visibility = null, context = null, onOpenChange, onChatStateChange, onAppLinkClick,
|
|
21
|
+
export function HostChat({ ctx, resolvedApplications, pathname, activeAppId, visibility = null, context = null, onOpenChange, onChatStateChange, onAppLinkClick, }) {
|
|
22
22
|
const features = useHostChromeWorkspaceFeatures(resolvedApplications, ctx, pathname);
|
|
23
23
|
const shellTelemetry = useMemo(() => getAppLifecycleCallbacks()?.createTelemetryCallbacks?.("host-ui"), []);
|
|
24
24
|
const chat = useHostChromeChat({
|
|
@@ -28,7 +28,6 @@ export function HostChat({ ctx, resolvedApplications, pathname, activeAppId, vis
|
|
|
28
28
|
dialogPosition: context?.dialogPosition,
|
|
29
29
|
embedded: context?.embedded,
|
|
30
30
|
onAppLinkClick,
|
|
31
|
-
onAppEventReceive,
|
|
32
31
|
});
|
|
33
32
|
const { askAiAssistant: chatAskAiAssistant, openAiAssistant: chatOpenAiAssistant, open: chatOpen, close: chatClose, toggle: chatToggle, setTags: chatSetTags, setAmbientUserContext: chatSetAmbientUserContext, isOpen: chatIsOpen, showChatItem, } = chat;
|
|
34
33
|
// Report the chat open-state outward so the runtime can forward it to the active pluggable
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"HostChrome.d.ts","sourceRoot":"","sources":["../../src/ui/HostChrome.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAuC,KAAK,SAAS,EAAwB,MAAM,OAAO,CAAC;AAElG,OAAO,EAEH,KAAK,iBAAiB,EAEtB,KAAK,gCAAgC,EACxC,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EACH,KAAK,iBAAiB,EACtB,KAAK,mBAAmB,EACxB,KAAK,gBAAgB,EACxB,MAAM,2CAA2C,CAAC;AA+BnD,OAAO,mBAAmB,CAAC;AAK3B,OAAO,0CAA0C,CAAC;AAClD,OAAO,6CAA6C,CAAC;AACrD,OAAO,sDAAsD,CAAC;AAC9D,OAAO,0DAA0D,CAAC;
|
|
1
|
+
{"version":3,"file":"HostChrome.d.ts","sourceRoot":"","sources":["../../src/ui/HostChrome.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAuC,KAAK,SAAS,EAAwB,MAAM,OAAO,CAAC;AAElG,OAAO,EAEH,KAAK,iBAAiB,EAEtB,KAAK,gCAAgC,EACxC,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EACH,KAAK,iBAAiB,EACtB,KAAK,mBAAmB,EACxB,KAAK,gBAAgB,EACxB,MAAM,2CAA2C,CAAC;AA+BnD,OAAO,mBAAmB,CAAC;AAK3B,OAAO,0CAA0C,CAAC;AAClD,OAAO,6CAA6C,CAAC;AACrD,OAAO,sDAAsD,CAAC;AAC9D,OAAO,0DAA0D,CAAC;AAelE,MAAM,WAAW,gBAAgB;IAC7B,GAAG,EAAE,gBAAgB,CAAC;IACtB,oBAAoB,EAAE,gCAAgC,EAAE,CAAC;IACzD,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IAClC,SAAS,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IACjC,aAAa,CAAC,EAAE,iBAAiB,CAAC;IAClC,YAAY,CAAC,EAAE,mBAAmB,GAAG,IAAI,CAAC;IAC1C;;;OAGG;IACH,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,4FAA4F;IAC5F,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,oEAAoE;IACpE,YAAY,CAAC,EAAE,MAAM,IAAI,CAAC;IAC1B,2FAA2F;IAC3F,gBAAgB,CAAC,EAAE,CACf,QAAQ,EAAE,MAAM,EAChB,OAAO,CAAC,EAAE,MAAM,EAChB,WAAW,CAAC,EAAE,iBAAiB,EAC/B,YAAY,CAAC,EAAE,OAAO,EACtB,kBAAkB,CAAC,EAAE,OAAO,KAC3B,IAAI,CAAC;IACV;;;OAGG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,EAAE,SAAS,CAAC;CACxB;AAED,wBAAgB,UAAU,CAAC,EACvB,GAAG,EACH,oBAAoB,EACpB,QAAQ,EACR,UAAU,EACV,SAAS,EAAE,UAAU,EACrB,aAAa,EACb,YAAmB,EACnB,YAAoB,EACpB,YAAY,EACZ,gBAAgB,EAChB,YAAY,EACZ,QAAQ,EACX,EAAE,gBAAgB,2CA0OlB"}
|
package/esm/ui/HostChrome.js
CHANGED
|
@@ -11,7 +11,7 @@ import { getAppLifecycleCallbacks, preloadPluggableApplication, } from "../loade
|
|
|
11
11
|
import { getActiveInternalApplication, getApplicationHref } from "../loader/routing.js";
|
|
12
12
|
import { getBackend } from "../platformContext/backend.js";
|
|
13
13
|
import { buildAppMenu, getLocalizedTitle } from "./appMenuItems.js";
|
|
14
|
-
import { getUserDisplayName, swapWorkspaceInPath } from "./chromeHelpers.js";
|
|
14
|
+
import { getUserDisplayName, isPlainLeftClick, swapWorkspaceInPath } from "./chromeHelpers.js";
|
|
15
15
|
import { b, e } from "./hostChromeBem.js";
|
|
16
16
|
import { HostIntlProvider } from "./HostIntlProvider.js";
|
|
17
17
|
import { HostNotificationDispatcher } from "./HostNotificationDispatcher.js";
|
|
@@ -35,6 +35,8 @@ const LOGOUT_MENU_ITEM_KEY = "gs.header.logout";
|
|
|
35
35
|
// favicon is configured (see faviconUrl below).
|
|
36
36
|
const initialFaviconUrl = (typeof document !== "undefined" && document.querySelector("link[rel~='icon']")?.getAttribute("href")) ||
|
|
37
37
|
"/favicon.ico";
|
|
38
|
+
// Switches the host scope to organization; the first organization app is then chosen by redirect logic.
|
|
39
|
+
const LOGO_HREF = "/organization";
|
|
38
40
|
export function HostChrome({ ctx, resolvedApplications, pathname, onNavigate, onReplace: _onReplace, headerOptions, notification = null, showChatItem = false, onChatToggle, onAskAiAssistant, appPageTitle, children, }) {
|
|
39
41
|
const locale = resolveLocale(ctx.preferredLocale);
|
|
40
42
|
const userName = getUserDisplayName(ctx.user);
|
|
@@ -100,6 +102,14 @@ export function HostChrome({ ctx, resolvedApplications, pathname, onNavigate, on
|
|
|
100
102
|
}
|
|
101
103
|
}
|
|
102
104
|
}, [resolvedApplications, ctx, pathname]);
|
|
105
|
+
// Without this the anchor would do a full page load instead of a client-side navigation.
|
|
106
|
+
const handleLogoClick = useCallback((e) => {
|
|
107
|
+
if (!isPlainLeftClick(e)) {
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
e.preventDefault();
|
|
111
|
+
onNavigate(LOGO_HREF);
|
|
112
|
+
}, [onNavigate]);
|
|
103
113
|
const handleWorkspaceSelect = useCallback((workspace) => {
|
|
104
114
|
const newId = workspace.id;
|
|
105
115
|
if (!newId) {
|
|
@@ -143,7 +153,6 @@ export function HostChrome({ ctx, resolvedApplications, pathname, onNavigate, on
|
|
|
143
153
|
const activeColor = ctx.theme?.header?.activeColor ?? defaultHeaderTheme.activeColor;
|
|
144
154
|
const hideChrome = ctx.embeddingMode === "iframe" || ctx.isExportMode === true;
|
|
145
155
|
return (_jsx(HostIntlProvider, { locale: locale, additionalMessages: appMessages, children: _jsx(BackendProvider, { backend: getBackend(), children: _jsx(ToastsCenterContextProvider, { children: _jsxs("div", { className: b(), children: [
|
|
146
|
-
_jsx(DocumentHeader, { pageTitle: documentPageTitle, brandTitle: documentBrandTitle, faviconUrl: faviconUrl, appleTouchIconUrl: ctx.whiteLabeling?.appleTouchIconUrl }), hideChrome ? null : (_jsx("div", { className: e("header"), onMouseOver: handleHeaderMouseOver, children: _jsx(AppHeader, { logoUrl: ctx.whiteLabeling?.logoUrl || defaultLogoUrl, logoHref:
|
|
147
|
-
, logoTitle: logoTitle, headerColor: headerColor, headerTextColor: headerTextColor, activeColor: activeColor, userName: userName, organizationName: ctx.organization?.title, isAccessibilityCompliant: true, workspacePicker: workspacePicker, menuItemsGroups: menuItemsGroups, helpMenuItems: helpMenuItems, accountMenuItems: accountMenuItems, onMenuItemClick: handleMenuItemClick, showUpsellButton: pricing.isTrial, onUpsellButtonClick: pricing.onUpsellButtonClick, expiredDate: pricing.isTrial ? pricing.expiredDate : undefined, search: search.element, showChatItem: showChatItem, onChatItemClick: onChatToggle, notificationsPanel: ({ isMobile, closeNotificationsOverlay }) => (_jsx(AppHeaderNotifications, { locale: locale, isMobile: isMobile, closeNotificationsOverlay: closeNotificationsOverlay, useAsOfDateParam: true, enableExportToDocumentStorage: ctx.userSettings.enableExportToDocumentStorage ?? false })) }) })), _jsx("main", { className: e("content"), children: children }), pricing.element, _jsx(HostNotificationDispatcher, { notification: notification })
|
|
156
|
+
_jsx(DocumentHeader, { pageTitle: documentPageTitle, brandTitle: documentBrandTitle, faviconUrl: faviconUrl, appleTouchIconUrl: ctx.whiteLabeling?.appleTouchIconUrl }), hideChrome ? null : (_jsx("div", { className: e("header"), onMouseOver: handleHeaderMouseOver, children: _jsx(AppHeader, { logoUrl: ctx.whiteLabeling?.logoUrl || defaultLogoUrl, logoHref: LOGO_HREF, onLogoClick: handleLogoClick, logoTitle: logoTitle, headerColor: headerColor, headerTextColor: headerTextColor, activeColor: activeColor, userName: userName, organizationName: ctx.organization?.title, isAccessibilityCompliant: true, workspacePicker: workspacePicker, menuItemsGroups: menuItemsGroups, helpMenuItems: helpMenuItems, accountMenuItems: accountMenuItems, onMenuItemClick: handleMenuItemClick, showUpsellButton: pricing.isTrial, onUpsellButtonClick: pricing.onUpsellButtonClick, expiredDate: pricing.isTrial ? pricing.expiredDate : undefined, search: search.element, showChatItem: showChatItem, onChatItemClick: onChatToggle, notificationsPanel: ({ isMobile, closeNotificationsOverlay }) => (_jsx(AppHeaderNotifications, { locale: locale, isMobile: isMobile, closeNotificationsOverlay: closeNotificationsOverlay, useAsOfDateParam: true, enableExportToDocumentStorage: ctx.userSettings.enableExportToDocumentStorage ?? false })) }) })), _jsx("main", { className: e("content"), children: children }), pricing.element, _jsx(HostNotificationDispatcher, { notification: notification })
|
|
148
157
|
] }) }) }) }));
|
|
149
158
|
}
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import { type RefObject } from "react";
|
|
2
2
|
import { type IGenAIUserContext, type PluggableApplicationRegistryItem } from "@gooddata/sdk-model";
|
|
3
|
-
import { type IAppHeaderOptions, type IPlatformContext } from "@gooddata/sdk-pluggable-application-model";
|
|
3
|
+
import { type IAppHeaderOptions, type IHostNavigationRequest, type IPlatformContext } from "@gooddata/sdk-pluggable-application-model";
|
|
4
4
|
import "./PluggableApplicationRenderer.scss";
|
|
5
|
-
import { GenAIChatEvent } from "./GenAIChat.js";
|
|
6
5
|
export interface IPluggableApplicationRendererProps {
|
|
7
6
|
app: PluggableApplicationRegistryItem;
|
|
8
7
|
ctx: IPlatformContext;
|
|
@@ -32,12 +31,13 @@ export interface IPluggableApplicationRendererProps {
|
|
|
32
31
|
newTab?: boolean;
|
|
33
32
|
}) => boolean) | undefined>;
|
|
34
33
|
/**
|
|
35
|
-
* Ref the renderer populates with a handler that
|
|
36
|
-
*
|
|
34
|
+
* Ref the renderer populates with a handler that asks the active app's mount handle
|
|
35
|
+
* (`onHostNavigationRequested`) whether the host may navigate away. Must be referentially
|
|
36
|
+
* stable — a new ref object remounts the application.
|
|
37
37
|
*/
|
|
38
|
-
|
|
38
|
+
navigationRequestRef?: RefObject<((request: IHostNavigationRequest) => boolean) | undefined>;
|
|
39
39
|
onHeaderChange?: (appId: string, header: IAppHeaderOptions) => void;
|
|
40
40
|
onDocumentTitleChange?: (appId: string, pageTitle: string | undefined) => void;
|
|
41
41
|
}
|
|
42
|
-
export declare function PluggableApplicationRenderer({ app, ctx, pathname, aiAssistantOpen, onOpenAiAssistant, onCloseAiAssistant, onAiAssistantContext, aiLinkClickHandlerRef,
|
|
42
|
+
export declare function PluggableApplicationRenderer({ app, ctx, pathname, aiAssistantOpen, onOpenAiAssistant, onCloseAiAssistant, onAiAssistantContext, aiLinkClickHandlerRef, navigationRequestRef, onHeaderChange, onDocumentTitleChange }: IPluggableApplicationRendererProps): import("react/jsx-runtime").JSX.Element;
|
|
43
43
|
//# sourceMappingURL=PluggableApplicationRenderer.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PluggableApplicationRenderer.d.ts","sourceRoot":"","sources":["../../src/ui/PluggableApplicationRenderer.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,SAAS,EAAqD,MAAM,OAAO,CAAC;AAI1F,OAAO,EAAE,KAAK,iBAAiB,EAAE,KAAK,gCAAgC,EAAE,MAAM,qBAAqB,CAAC;AACpG,OAAO,EACH,KAAK,iBAAiB,EACtB,KAAK,gBAAgB,EASxB,MAAM,2CAA2C,CAAC;AAcnD,OAAO,qCAAqC,CAAC;
|
|
1
|
+
{"version":3,"file":"PluggableApplicationRenderer.d.ts","sourceRoot":"","sources":["../../src/ui/PluggableApplicationRenderer.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,SAAS,EAAqD,MAAM,OAAO,CAAC;AAI1F,OAAO,EAAE,KAAK,iBAAiB,EAAE,KAAK,gCAAgC,EAAE,MAAM,qBAAqB,CAAC;AACpG,OAAO,EACH,KAAK,iBAAiB,EACtB,KAAK,sBAAsB,EAC3B,KAAK,gBAAgB,EASxB,MAAM,2CAA2C,CAAC;AAcnD,OAAO,qCAAqC,CAAC;AAkB7C,MAAM,WAAW,kCAAkC;IAC/C,GAAG,EAAE,gCAAgC,CAAC;IACtC,GAAG,EAAE,gBAAgB,CAAC;IACtB,QAAQ,EAAE,MAAM,CAAC;IACjB,sFAAsF;IACtF,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,6FAA6F;IAC7F,iBAAiB,CAAC,EAAE,CAChB,QAAQ,CAAC,EAAE,MAAM,EACjB,OAAO,CAAC,EAAE,MAAM,EAChB,WAAW,CAAC,EAAE,iBAAiB,EAC/B,YAAY,CAAC,EAAE,OAAO,EACtB,kBAAkB,CAAC,EAAE,OAAO,KAC3B,IAAI,CAAC;IACV,8DAA8D;IAC9D,kBAAkB,CAAC,EAAE,MAAM,IAAI,CAAC;IAChC,oHAAoH;IACpH,oBAAoB,CAAC,EAAE,CAAC,OAAO,EAAE;QAC7B,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;QACvB,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;QACvB,cAAc,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;QAClC,QAAQ,CAAC,EAAE,OAAO,CAAC;QACnB,WAAW,CAAC,EAAE,iBAAiB,CAAC;KACnC,KAAK,IAAI,CAAC;IACX;;;OAGG;IACH,qBAAqB,CAAC,EAAE,SAAS,CAC7B,CAAC,CAAC,IAAI,EAAE;QAAE,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,EAAE,CAAC,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,OAAO,CAAA;KAAE,KAAK,OAAO,CAAC,GAAG,SAAS,CACtG,CAAC;IACF;;;;OAIG;IACH,oBAAoB,CAAC,EAAE,SAAS,CAAC,CAAC,CAAC,OAAO,EAAE,sBAAsB,KAAK,OAAO,CAAC,GAAG,SAAS,CAAC,CAAC;IAC7F,cAAc,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,iBAAiB,KAAK,IAAI,CAAC;IACpE,qBAAqB,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,SAAS,KAAK,IAAI,CAAC;CAClF;AAED,wBAAgB,4BAA4B,CAAC,EACzC,GAAG,EACH,GAAG,EACH,QAAQ,EACR,eAAe,EACf,iBAAiB,EACjB,kBAAkB,EAClB,oBAAoB,EACpB,qBAAqB,EACrB,oBAAoB,EACpB,cAAc,EACd,qBAAqB,EACxB,EAAE,kCAAkC,2CA8RpC"}
|
|
@@ -20,7 +20,7 @@ const SECURITY_FAILURE_MESSAGES = {
|
|
|
20
20
|
"build-expired": defineMessage({ id: "gs.host.error.appBuildExpired" }),
|
|
21
21
|
"metadata-missing": defineMessage({ id: "gs.host.error.appSecurityMetadataMissing" }),
|
|
22
22
|
};
|
|
23
|
-
export function PluggableApplicationRenderer({ app, ctx, pathname, aiAssistantOpen, onOpenAiAssistant, onCloseAiAssistant, onAiAssistantContext, aiLinkClickHandlerRef,
|
|
23
|
+
export function PluggableApplicationRenderer({ app, ctx, pathname, aiAssistantOpen, onOpenAiAssistant, onCloseAiAssistant, onAiAssistantContext, aiLinkClickHandlerRef, navigationRequestRef, onHeaderChange, onDocumentTitleChange, }) {
|
|
24
24
|
const intl = useIntl();
|
|
25
25
|
const intlRef = useAutoupdateRef(intl);
|
|
26
26
|
const ctxRef = useAutoupdateRef(ctx);
|
|
@@ -83,12 +83,14 @@ export function PluggableApplicationRenderer({ app, ctx, pathname, aiAssistantOp
|
|
|
83
83
|
let cancelled = false;
|
|
84
84
|
const mountId = `${app.id}:${Date.now()}`;
|
|
85
85
|
const totalStart = now();
|
|
86
|
+
// Identity must change per mount — the host rejects a `proceed` retained from an earlier one.
|
|
87
|
+
const navigationRequest = (request) => mountHandleRef.current?.onHostNavigationRequested?.(request) ?? false;
|
|
86
88
|
const prevHandle = mountHandleRef.current;
|
|
87
89
|
mountHandleRef.current = undefined;
|
|
88
90
|
mountedAppRef.current = undefined;
|
|
89
91
|
setViewState({ state: "loading" });
|
|
90
|
-
//
|
|
91
|
-
//
|
|
92
|
+
// unmount() is synchronous, so calling it inline can re-enter React while the parent render
|
|
93
|
+
// that swapped this application out is still in progress.
|
|
92
94
|
if (prevHandle) {
|
|
93
95
|
queueMicrotask(() => prevHandle.unmount());
|
|
94
96
|
}
|
|
@@ -126,6 +128,9 @@ export function PluggableApplicationRenderer({ app, ctx, pathname, aiAssistantOp
|
|
|
126
128
|
onHeaderChange: (header) => onHeaderChangeRef.current?.(app.id, header),
|
|
127
129
|
});
|
|
128
130
|
mountedAppRef.current = { app, loadedApp };
|
|
131
|
+
if (navigationRequestRef) {
|
|
132
|
+
navigationRequestRef.current = navigationRequest;
|
|
133
|
+
}
|
|
129
134
|
lifecycle?.onMountCompleted?.(app.id, now() - mountStart);
|
|
130
135
|
lifecycle?.onRendered?.(app.id, now() - totalStart);
|
|
131
136
|
setViewState({ state: "ready", validUntil: getSecuredRemoteAppValidUntil(app, loadedApp) });
|
|
@@ -134,6 +139,11 @@ export function PluggableApplicationRenderer({ app, ctx, pathname, aiAssistantOp
|
|
|
134
139
|
if (cancelled) {
|
|
135
140
|
return;
|
|
136
141
|
}
|
|
142
|
+
// An application shown as failed must not veto navigation — its dialog would render
|
|
143
|
+
// into a hidden container.
|
|
144
|
+
if (navigationRequestRef?.current === navigationRequest) {
|
|
145
|
+
navigationRequestRef.current = undefined;
|
|
146
|
+
}
|
|
137
147
|
const errorMessage = mountError instanceof Error ? mountError.message : "Unknown module loading error.";
|
|
138
148
|
console.error(`[host-runtime/renderer] Failed to mount app "${app.id}".`, mountError);
|
|
139
149
|
lifecycle?.onLoadFailed?.(app.id, errorMessage);
|
|
@@ -144,6 +154,10 @@ export function PluggableApplicationRenderer({ app, ctx, pathname, aiAssistantOp
|
|
|
144
154
|
});
|
|
145
155
|
return () => {
|
|
146
156
|
cancelled = true;
|
|
157
|
+
// Only ever clear this mount's own registration, never a newer mount's.
|
|
158
|
+
if (navigationRequestRef?.current === navigationRequest) {
|
|
159
|
+
navigationRequestRef.current = undefined;
|
|
160
|
+
}
|
|
147
161
|
const handle = mountHandleRef.current;
|
|
148
162
|
if (handle) {
|
|
149
163
|
mountHandleRef.current = undefined;
|
|
@@ -154,7 +168,17 @@ export function PluggableApplicationRenderer({ app, ctx, pathname, aiAssistantOp
|
|
|
154
168
|
});
|
|
155
169
|
}
|
|
156
170
|
};
|
|
157
|
-
}, [
|
|
171
|
+
}, [
|
|
172
|
+
app,
|
|
173
|
+
appBasePath,
|
|
174
|
+
ctxRef,
|
|
175
|
+
intlRef,
|
|
176
|
+
onHeaderChangeRef,
|
|
177
|
+
onTelemetryEvent,
|
|
178
|
+
lifecycle,
|
|
179
|
+
onEvent,
|
|
180
|
+
navigationRequestRef,
|
|
181
|
+
]);
|
|
158
182
|
useEffect(() => {
|
|
159
183
|
const mounted = mountedAppRef.current;
|
|
160
184
|
const handle = mountHandleRef.current;
|
|
@@ -171,6 +195,9 @@ export function PluggableApplicationRenderer({ app, ctx, pathname, aiAssistantOp
|
|
|
171
195
|
console.error(`[host-runtime/renderer] Unmounting app "${mounted.app.id}" after context change: ${securityFailure.kind}.`, securityFailure);
|
|
172
196
|
mountHandleRef.current = undefined;
|
|
173
197
|
mountedAppRef.current = undefined;
|
|
198
|
+
if (navigationRequestRef) {
|
|
199
|
+
navigationRequestRef.current = undefined;
|
|
200
|
+
}
|
|
174
201
|
handle.unmount();
|
|
175
202
|
lifecycle?.onUnmounted?.(mounted.app.id);
|
|
176
203
|
lifecycle?.onLoadFailed?.(mounted.app.id, securityFailure.kind);
|
|
@@ -178,7 +205,7 @@ export function PluggableApplicationRenderer({ app, ctx, pathname, aiAssistantOp
|
|
|
178
205
|
return;
|
|
179
206
|
}
|
|
180
207
|
handle.updateContext?.(ctx);
|
|
181
|
-
}, [ctx, intlRef, lifecycle]);
|
|
208
|
+
}, [ctx, intlRef, lifecycle, navigationRequestRef]);
|
|
182
209
|
// Forward the host-owned chat open-state to the mounted app once it is ready, and on every
|
|
183
210
|
// change, so app-side assistant controls stay aligned with the real (host) state (LX-2544).
|
|
184
211
|
useEffect(() => {
|
|
@@ -200,19 +227,6 @@ export function PluggableApplicationRenderer({ app, ctx, pathname, aiAssistantOp
|
|
|
200
227
|
ref.current = undefined;
|
|
201
228
|
};
|
|
202
229
|
}, [aiLinkClickHandlerRef]);
|
|
203
|
-
// Expose a host-chat event delegate that defers to the active app's mount handle, so an
|
|
204
|
-
// embedded app can handle events in-app. Read through the ref at call time so it
|
|
205
|
-
// always targets the currently mounted app.
|
|
206
|
-
useEffect(() => {
|
|
207
|
-
const ref = aiEventReceiveRef;
|
|
208
|
-
if (!ref) {
|
|
209
|
-
return;
|
|
210
|
-
}
|
|
211
|
-
ref.current = (event) => mountHandleRef.current?.onAiAssistantEventReceive?.(event);
|
|
212
|
-
return () => {
|
|
213
|
-
ref.current = undefined;
|
|
214
|
-
};
|
|
215
|
-
}, [aiEventReceiveRef]);
|
|
216
230
|
return (_jsxs("section", { className: b(), children: [viewState.state === "loading" ? (_jsx("div", { className: e("loading"), children: _jsx(LoadingComponent, { height: 40 }) })) : null, viewState.state === "error" ? (_jsxs("div", { className: e("error"), children: [
|
|
217
231
|
_jsx("h2", { children: _jsx(FormattedMessage, { id: "gs.host.error.applicationFailedToLoad" }) }), _jsx("p", { children: viewState.message })
|
|
218
232
|
] })) : null, _jsx("div", { ref: containerRef, className: e("container", { visible: viewState.state === "ready" }) }), viewState.state === "ready" && viewState.validUntil !== undefined ? (_jsx("div", { className: e("demoBadge"), role: "status", children: _jsx(FormattedMessage, { id: "gs.host.demoApp.validUntil", values: {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"appMenuItems.d.ts","sourceRoot":"","sources":["../../src/ui/appMenuItems.ts"],"names":[],"mappings":"AAIA,OAAO,EACH,KAAK,OAAO,EACZ,KAAK,gCAAgC,EAExC,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAwB,KAAK,gBAAgB,EAAE,MAAM,2CAA2C,CAAC;AACxG,OAAO,EAAE,KAAK,eAAe,EAAE,MAAM,sBAAsB,CAAC;
|
|
1
|
+
{"version":3,"file":"appMenuItems.d.ts","sourceRoot":"","sources":["../../src/ui/appMenuItems.ts"],"names":[],"mappings":"AAIA,OAAO,EACH,KAAK,OAAO,EACZ,KAAK,gCAAgC,EAExC,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAwB,KAAK,gBAAgB,EAAE,MAAM,2CAA2C,CAAC;AACxG,OAAO,EAAE,KAAK,eAAe,EAAE,MAAM,sBAAsB,CAAC;AA2B5D,wBAAgB,iBAAiB,CAC7B,GAAG,EAAE,gCAAgC,EACrC,MAAM,EAAE,OAAO,GAAG,SAAS,GAC5B,MAAM,CAQR;AAED,MAAM,WAAW,cAAc;IAC3B,eAAe,EAAE,eAAe,EAAE,EAAE,CAAC;IACrC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACpC;AAeD;;;;;;;;GAQG;AACH,wBAAgB,YAAY,CACxB,IAAI,EAAE,gCAAgC,EAAE,EACxC,GAAG,EAAE,gBAAgB,EACrB,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,OAAO,GAAG,SAAS,GAC5B,cAAc,CA4BhB"}
|
package/esm/ui/appMenuItems.js
CHANGED
|
@@ -2,18 +2,12 @@
|
|
|
2
2
|
import { isExternalPluggableApplicationRegistryItem, } from "@gooddata/sdk-model";
|
|
3
3
|
import { DefaultApplicationId } from "@gooddata/sdk-pluggable-application-model";
|
|
4
4
|
import { getApplicationHref, isInternalAppRouteActive } from "../loader/routing.js";
|
|
5
|
+
import { isPlainLeftClick } from "./chromeHelpers.js";
|
|
5
6
|
function handleExternalAppClick(href) {
|
|
6
7
|
return (event) => {
|
|
7
|
-
//
|
|
8
|
-
//
|
|
9
|
-
|
|
10
|
-
// external apps live outside the host SPA, so React Router can't reach them.
|
|
11
|
-
if (event.defaultPrevented ||
|
|
12
|
-
event.button !== 0 ||
|
|
13
|
-
event.metaKey ||
|
|
14
|
-
event.ctrlKey ||
|
|
15
|
-
event.shiftKey ||
|
|
16
|
-
event.altKey) {
|
|
8
|
+
// Suppresses the parent menu's SPA navigation in favour of a full page load: external
|
|
9
|
+
// apps live outside the host SPA, so React Router cannot reach them.
|
|
10
|
+
if (!isPlainLeftClick(event)) {
|
|
17
11
|
return;
|
|
18
12
|
}
|
|
19
13
|
event.preventDefault();
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type MouseEvent } from "react";
|
|
1
2
|
import { type IUser } from "@gooddata/sdk-model";
|
|
2
3
|
/**
|
|
3
4
|
* Builds the display name shown for the current user in the host header.
|
|
@@ -15,4 +16,12 @@ export declare function getUserDisplayName(user: IUser): string;
|
|
|
15
16
|
* an organization-scoped route).
|
|
16
17
|
*/
|
|
17
18
|
export declare function swapWorkspaceInPath(pathname: string, newWorkspaceId: string): string;
|
|
19
|
+
/**
|
|
20
|
+
* Whether a click on a link may be taken over with `preventDefault`, rather than left to the
|
|
21
|
+
* browser's own handling of the href.
|
|
22
|
+
*
|
|
23
|
+
* @remarks
|
|
24
|
+
* Keyboard activation of a focused link reports button 0, so it counts as a plain left click.
|
|
25
|
+
*/
|
|
26
|
+
export declare function isPlainLeftClick(event: MouseEvent<Element>): boolean;
|
|
18
27
|
//# sourceMappingURL=chromeHelpers.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"chromeHelpers.d.ts","sourceRoot":"","sources":["../../src/ui/chromeHelpers.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,KAAK,EAAE,MAAM,qBAAqB,CAAC;AAEjD;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,KAAK,GAAG,MAAM,CAStD;AAED;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,GAAG,MAAM,CAGpF"}
|
|
1
|
+
{"version":3,"file":"chromeHelpers.d.ts","sourceRoot":"","sources":["../../src/ui/chromeHelpers.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,UAAU,EAAE,MAAM,OAAO,CAAC;AAExC,OAAO,EAAE,KAAK,KAAK,EAAE,MAAM,qBAAqB,CAAC;AAEjD;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,KAAK,GAAG,MAAM,CAStD;AAED;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,GAAG,MAAM,CAGpF;AAED;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,UAAU,CAAC,OAAO,CAAC,GAAG,OAAO,CASpE"}
|
package/esm/ui/chromeHelpers.js
CHANGED
|
@@ -27,3 +27,18 @@ export function swapWorkspaceInPath(pathname, newWorkspaceId) {
|
|
|
27
27
|
const replaced = pathname.replace(/^\/workspace\/[^/]+/, `/workspace/${newWorkspaceId}`);
|
|
28
28
|
return replaced === pathname ? `/workspace/${newWorkspaceId}` : replaced;
|
|
29
29
|
}
|
|
30
|
+
/**
|
|
31
|
+
* Whether a click on a link may be taken over with `preventDefault`, rather than left to the
|
|
32
|
+
* browser's own handling of the href.
|
|
33
|
+
*
|
|
34
|
+
* @remarks
|
|
35
|
+
* Keyboard activation of a focused link reports button 0, so it counts as a plain left click.
|
|
36
|
+
*/
|
|
37
|
+
export function isPlainLeftClick(event) {
|
|
38
|
+
return (!event.defaultPrevented &&
|
|
39
|
+
event.button === 0 &&
|
|
40
|
+
!event.metaKey &&
|
|
41
|
+
!event.ctrlKey &&
|
|
42
|
+
!event.shiftKey &&
|
|
43
|
+
!event.altKey);
|
|
44
|
+
}
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { type ReactNode } from "react";
|
|
2
2
|
import { type IGenAIUserContext } from "@gooddata/sdk-model";
|
|
3
3
|
import { type IPlatformContext, type IPluggableAppTelemetryCallbacks } from "@gooddata/sdk-pluggable-application-model";
|
|
4
|
-
import { type GenAIChatEvent } from "./GenAIChat.js";
|
|
5
4
|
import { type IHostChromeWorkspaceFeatures } from "./useHostChromeWorkspaceFeatures.js";
|
|
6
5
|
export interface IHostChromeChat {
|
|
7
6
|
/** The `<GenAIChat>` element to mount, or `null` when chat is gated off. */
|
|
@@ -55,8 +54,6 @@ export interface IUseHostChromeChatArgs {
|
|
|
55
54
|
itemUrl?: string;
|
|
56
55
|
newTab?: boolean;
|
|
57
56
|
}) => boolean;
|
|
58
|
-
/** Delegates a chat event receive to the active app. */
|
|
59
|
-
onAppEventReceive?: (event: GenAIChatEvent) => void;
|
|
60
57
|
}
|
|
61
58
|
/**
|
|
62
59
|
* Owns the GenAI chat state and renders the `<GenAIChat>` element for the host chrome.
|
|
@@ -65,5 +62,5 @@ export interface IUseHostChromeChatArgs {
|
|
|
65
62
|
* points, the runtime availability probe (`useGenAiChatAvailability`) and forwarding chat
|
|
66
63
|
* events to the host telemetry callbacks.
|
|
67
64
|
*/
|
|
68
|
-
export declare function useHostChromeChat({ features, ctx, telemetry, dialogPosition, embedded, onAppLinkClick
|
|
65
|
+
export declare function useHostChromeChat({ features, ctx, telemetry, dialogPosition, embedded, onAppLinkClick }: IUseHostChromeChatArgs): IHostChromeChat;
|
|
69
66
|
//# sourceMappingURL=useHostChromeChat.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useHostChromeChat.d.ts","sourceRoot":"","sources":["../../src/ui/useHostChromeChat.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,SAAS,EAAyB,MAAM,OAAO,CAAC;AAE9D,OAAO,EAAE,KAAK,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAC7D,OAAO,EACH,KAAK,gBAAgB,EACrB,KAAK,+BAA+B,EACvC,MAAM,2CAA2C,CAAC;
|
|
1
|
+
{"version":3,"file":"useHostChromeChat.d.ts","sourceRoot":"","sources":["../../src/ui/useHostChromeChat.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,SAAS,EAAyB,MAAM,OAAO,CAAC;AAE9D,OAAO,EAAE,KAAK,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAC7D,OAAO,EACH,KAAK,gBAAgB,EACrB,KAAK,+BAA+B,EACvC,MAAM,2CAA2C,CAAC;AAMnD,OAAO,EAAE,KAAK,4BAA4B,EAAE,MAAM,qCAAqC,CAAC;AAExF,MAAM,WAAW,eAAe;IAC5B,4EAA4E;IAC5E,OAAO,EAAE,SAAS,CAAC;IACnB,iDAAiD;IACjD,MAAM,EAAE,OAAO,CAAC;IAChB;;;OAGG;IACH,YAAY,EAAE,OAAO,CAAC;IACtB,4BAA4B;IAC5B,IAAI,EAAE,MAAM,IAAI,CAAC;IACjB,6BAA6B;IAC7B,KAAK,EAAE,MAAM,IAAI,CAAC;IAClB,6EAA2E;IAC3E,MAAM,EAAE,MAAM,IAAI,CAAC;IACnB;;;OAGG;IACH,cAAc,EAAE,CACZ,QAAQ,EAAE,MAAM,EAChB,OAAO,CAAC,EAAE,MAAM,EAChB,WAAW,CAAC,EAAE,iBAAiB,EAC/B,YAAY,CAAC,EAAE,OAAO,EACtB,kBAAkB,CAAC,EAAE,OAAO,KAC3B,IAAI,CAAC;IACV;;;OAGG;IACH,eAAe,EAAE,CACb,WAAW,CAAC,EAAE,iBAAiB,EAC/B,kBAAkB,CAAC,EAAE,OAAO,EAC5B,OAAO,CAAC,EAAE,MAAM,KACf,IAAI,CAAC;IACV;;;OAGG;IACH,OAAO,EAAE,CAAC,WAAW,CAAC,EAAE,MAAM,EAAE,EAAE,WAAW,CAAC,EAAE,MAAM,EAAE,KAAK,IAAI,CAAC;IAClE;;;OAGG;IACH,qBAAqB,EAAE,CAAC,WAAW,CAAC,EAAE,iBAAiB,KAAK,IAAI,CAAC;CACpE;AAED,MAAM,WAAW,sBAAsB;IACnC,QAAQ,EAAE,4BAA4B,CAAC;IACvC,GAAG,EAAE,gBAAgB,CAAC;IACtB,SAAS,EAAE,+BAA+B,GAAG,SAAS,CAAC;IACvD,+FAA+F;IAC/F,cAAc,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;IAClC,0FAA0F;IAC1F,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,yFAAyF;IACzF,cAAc,CAAC,EAAE,CAAC,IAAI,EAAE;QAAE,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,EAAE,CAAC,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,OAAO,CAAA;KAAE,KAAK,OAAO,CAAC;CAC1G;AAED;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAAC,EAC9B,QAAQ,EACR,GAAG,EACH,SAAS,EACT,cAAc,EACd,QAAQ,EACR,cAAc,EACjB,EAAE,sBAAsB,GAAG,eAAe,CAwH1C"}
|
|
@@ -11,7 +11,7 @@ import { GenAIChat } from "./GenAIChat.js";
|
|
|
11
11
|
* points, the runtime availability probe (`useGenAiChatAvailability`) and forwarding chat
|
|
12
12
|
* events to the host telemetry callbacks.
|
|
13
13
|
*/
|
|
14
|
-
export function useHostChromeChat({ features, ctx, telemetry, dialogPosition, embedded, onAppLinkClick,
|
|
14
|
+
export function useHostChromeChat({ features, ctx, telemetry, dialogPosition, embedded, onAppLinkClick, }) {
|
|
15
15
|
const [isChatOpen, setIsChatOpen] = useState(false);
|
|
16
16
|
const [askedQuestion, setAskedQuestion] = useState(null);
|
|
17
17
|
const [appendToChat, setAppendToChat] = useState(false);
|
|
@@ -61,8 +61,7 @@ export function useHostChromeChat({ features, ctx, telemetry, dialogPosition, em
|
|
|
61
61
|
}, []);
|
|
62
62
|
const handleChatEvent = useCallback((event) => {
|
|
63
63
|
telemetry?.trackEvent(event.name, event.payload);
|
|
64
|
-
|
|
65
|
-
}, [telemetry, onAppEventReceive]);
|
|
64
|
+
}, [telemetry]);
|
|
66
65
|
const showChatItem = useGenAiChatAvailability(getBackend(), features.workspaceId, features.showChat, features.canManageProject);
|
|
67
66
|
const element = features.showChat && features.workspaceId ? (_jsx(GenAIChat, { workspaceId: features.workspaceId, open: isChatOpen, onOpen: open, onClose: close, askedQuestion: askedQuestion, appendToChat: appendToChat, agentId: agentId, replaceUserContext: replaceUserContext, askSeq: askSeq, userContext: userContext, ambientUserContext: ambientUserContext, includeTags: includeTags, excludeTags: excludeTags, canManageProject: features.canManageProject, canAnalyzeProject: features.canCreateVisualization, canFullControl: features.canFullControl, settings: ctx.workspaceSettings, dialogPosition: dialogPosition, embedded: embedded, onAppLinkClick: onAppLinkClick, onEvent: handleChatEvent })) : null;
|
|
68
67
|
return {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gooddata/sdk-ui-pluggable-host",
|
|
3
|
-
"version": "11.51.0
|
|
3
|
+
"version": "11.51.0",
|
|
4
4
|
"description": "GoodData SDK runtime for hosting pluggable applications — registry, loader, routing, platform context, default UI chrome",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "GoodData Corporation",
|
|
@@ -29,20 +29,20 @@
|
|
|
29
29
|
"dependencies": {
|
|
30
30
|
"@module-federation/runtime": "2.6.0",
|
|
31
31
|
"lodash-es": "^4.17.23",
|
|
32
|
-
"@gooddata/sdk-backend-base": "11.51.0
|
|
33
|
-
"@gooddata/sdk-
|
|
34
|
-
"@gooddata/sdk-backend-
|
|
35
|
-
"@gooddata/sdk-
|
|
36
|
-
"@gooddata/sdk-
|
|
37
|
-
"@gooddata/sdk-pluggable-application-model": "11.51.0
|
|
38
|
-
"@gooddata/sdk-ui": "11.51.0
|
|
39
|
-
"@gooddata/sdk-ui-
|
|
40
|
-
"@gooddata/sdk-ui-
|
|
41
|
-
"@gooddata/sdk-ui
|
|
42
|
-
"@gooddata/sdk-ui-kit": "11.51.0
|
|
43
|
-
"@gooddata/sdk-ui-
|
|
44
|
-
"@gooddata/sdk-ui-
|
|
45
|
-
"@gooddata/util": "11.51.0
|
|
32
|
+
"@gooddata/sdk-backend-base": "11.51.0",
|
|
33
|
+
"@gooddata/sdk-embedding": "11.51.0",
|
|
34
|
+
"@gooddata/sdk-backend-spi": "11.51.0",
|
|
35
|
+
"@gooddata/sdk-backend-tiger": "11.51.0",
|
|
36
|
+
"@gooddata/sdk-model": "11.51.0",
|
|
37
|
+
"@gooddata/sdk-pluggable-application-model": "11.51.0",
|
|
38
|
+
"@gooddata/sdk-ui-application-header": "11.51.0",
|
|
39
|
+
"@gooddata/sdk-ui-ext": "11.51.0",
|
|
40
|
+
"@gooddata/sdk-ui-gen-ai": "11.51.0",
|
|
41
|
+
"@gooddata/sdk-ui": "11.51.0",
|
|
42
|
+
"@gooddata/sdk-ui-kit": "11.51.0",
|
|
43
|
+
"@gooddata/sdk-ui-theme-provider": "11.51.0",
|
|
44
|
+
"@gooddata/sdk-ui-semantic-search": "11.51.0",
|
|
45
|
+
"@gooddata/util": "11.51.0"
|
|
46
46
|
},
|
|
47
47
|
"devDependencies": {
|
|
48
48
|
"@microsoft/api-documenter": "^7.17.0",
|
|
@@ -85,8 +85,8 @@
|
|
|
85
85
|
"vite": "8.0.16",
|
|
86
86
|
"vitest": "4.1.8",
|
|
87
87
|
"vitest-dom": "0.1.1",
|
|
88
|
-
"@gooddata/eslint-config": "11.51.0
|
|
89
|
-
"@gooddata/oxlint-config": "11.51.0
|
|
88
|
+
"@gooddata/eslint-config": "11.51.0",
|
|
89
|
+
"@gooddata/oxlint-config": "11.51.0"
|
|
90
90
|
},
|
|
91
91
|
"peerDependencies": {
|
|
92
92
|
"react": ">=18.3.1",
|