@gooddata/sdk-ui-pluggable-host 11.52.0-alpha.4 → 11.52.0-alpha.6

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.
@@ -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,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"}
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;AAKhC,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,2CAqT7F"}
@@ -10,8 +10,8 @@ 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 { resolveHostUiModule } from "../ui/resolveHostUiModule.js";
14
13
  import "./HostUiContainer.scss";
14
+ import { resolveHostUiModule } from "../ui/resolveHostUiModule.js";
15
15
  import { runGuardedNavigation } from "./navigationGuard.js";
16
16
  /**
17
17
  * Mounts the host UI module into a container div, then renders the active
@@ -64,6 +64,10 @@ export function HostUiContainer({ ctx, apps, pathname, routerNavigate }) {
64
64
  // app mount handle) and read by HostChat's chat so embedded apps can handle link clicks in-app.
65
65
  const appAiLinkClickRef = useRef(undefined);
66
66
  const onAppLinkClick = useCallback((link) => appAiLinkClickRef.current?.(link) ?? false, []);
67
+ // The active app's event handler, populated by PluggableApplicationRenderer (which holds the
68
+ // app mount handle) and read by HostChat's chat so embedded apps can handle events in-app.
69
+ const appEventReceiveRef = useRef(undefined);
70
+ const onAppEventReceive = useCallback((event) => appEventReceiveRef.current?.(event), []);
67
71
  const appNavigationRequestRef = useRef(undefined);
68
72
  const activeAppRef = useAutoupdateRef(activeInternalApplication);
69
73
  const onHeaderChange = useCallback((appId, header) => {
@@ -214,7 +218,7 @@ export function HostUiContainer({ ctx, apps, pathname, routerNavigate }) {
214
218
  return;
215
219
  }
216
220
  if (activeInternalApplication) {
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) }));
221
+ 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, aiEventReceiveRef: appEventReceiveRef, onHeaderChange: onHeaderChange, onDocumentTitleChange: onDocumentTitleChange }, activeInternalApplication.id) }));
218
222
  }
219
223
  else {
220
224
  appRootRef.current.render(null);
@@ -232,5 +236,5 @@ export function HostUiContainer({ ctx, apps, pathname, routerNavigate }) {
232
236
  setAiAssistantContext,
233
237
  ]);
234
238
  return (_jsxs(_Fragment, { children: [
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] }));
239
+ _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, onAppEventReceive: onAppEventReceive })) : null] }));
236
240
  }
@@ -0,0 +1,49 @@
1
+ import { type ModuleFederationRuntimePlugin } from "@module-federation/runtime";
2
+ /**
3
+ * Timeout applied to every `<link rel="preload">` Module Federation creates.
4
+ *
5
+ * MF's DOM helper hardcodes 20s (`createLink` in `@module-federation/sdk`). On a throttled
6
+ * connection a legitimate asset routinely needs longer, and hitting the timeout is doubly
7
+ * harmful: the resource is reported as a failure (surfacing as
8
+ * `preloadRemote failed to load N resource(s)`) *and* the `<link>` is removed from the
9
+ * document, which cancels the in-flight download. 60s is long enough for a slow but
10
+ * progressing fetch to land and warm the cache, and short enough that the promise does not
11
+ * stay pending indefinitely for a user who has long since navigated away.
12
+ */
13
+ export declare const PRELOAD_LINK_TIMEOUT_MS = 60000;
14
+ /**
15
+ * Key of the global map in which Module Federation caches remote-entry load promises,
16
+ * and the separator it composes the map keys with (`SEPARATOR` in `@module-federation/sdk`).
17
+ *
18
+ * The map sits on the global root, **not** under `globalThis.__FEDERATION__` where the
19
+ * instance/share/manifest registries live: `runtime-core`'s `global.js` does
20
+ * `definePropertyGlobalVal(CurrentGlobal, "__GLOBAL_LOADING_REMOTE_ENTRY__", {})` before it
21
+ * builds `__FEDERATION__`, and `CurrentGlobal` is `globalThis`. That asymmetry is easy to
22
+ * "correct" into a silent no-op, so `federationRuntimeGlobals.test.ts` pins it against the
23
+ * real runtime.
24
+ *
25
+ * Exported for that test.
26
+ */
27
+ export declare const GLOBAL_LOADING_REMOTE_ENTRY = "__GLOBAL_LOADING_REMOTE_ENTRY__";
28
+ /**
29
+ * Drops a remote entry's cached load promise so the next attempt starts from scratch.
30
+ *
31
+ * MF stores the promise returned by `getRemoteEntry` in a process-global map and — as of
32
+ * `@module-federation/runtime` 2.6.0 — only ever deletes it from `removeRemote`, never when
33
+ * the promise rejects. A single failed entry load, including one kicked off by a background
34
+ * hover preload the user never asked for, would therefore be replayed to every later caller
35
+ * and make that application unopenable for the lifetime of the page.
36
+ *
37
+ * Exported for tests.
38
+ */
39
+ export declare function evictRemoteEntryLoadCache(remoteInfo: {
40
+ name: string;
41
+ entry: string;
42
+ }): void;
43
+ /**
44
+ * Host-side Module Federation runtime plugin. Registered on the federation instance created
45
+ * by the remote loader; both hooks work around behaviour of the MF runtime we cannot
46
+ * configure any other way.
47
+ */
48
+ export declare function createHostFederationPlugin(): ModuleFederationRuntimePlugin;
49
+ //# sourceMappingURL=federationRuntimePlugin.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"federationRuntimePlugin.d.ts","sourceRoot":"","sources":["../../src/loader/federationRuntimePlugin.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,6BAA6B,EAAE,MAAM,4BAA4B,CAAC;AAEhF;;;;;;;;;;GAUG;AACH,eAAO,MAAM,uBAAuB,QAAS,CAAC;AAE9C;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,2BAA2B,oCAAoC,CAAC;AAK7E;;;;;;;;;;GAUG;AACH,wBAAgB,yBAAyB,CAAC,UAAU,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAU3F;AAED;;;;GAIG;AACH,wBAAgB,0BAA0B,IAAI,6BAA6B,CAgB1E"}
@@ -0,0 +1,66 @@
1
+ // (C) 2026 GoodData Corporation
2
+ /**
3
+ * Timeout applied to every `<link rel="preload">` Module Federation creates.
4
+ *
5
+ * MF's DOM helper hardcodes 20s (`createLink` in `@module-federation/sdk`). On a throttled
6
+ * connection a legitimate asset routinely needs longer, and hitting the timeout is doubly
7
+ * harmful: the resource is reported as a failure (surfacing as
8
+ * `preloadRemote failed to load N resource(s)`) *and* the `<link>` is removed from the
9
+ * document, which cancels the in-flight download. 60s is long enough for a slow but
10
+ * progressing fetch to land and warm the cache, and short enough that the promise does not
11
+ * stay pending indefinitely for a user who has long since navigated away.
12
+ */
13
+ export const PRELOAD_LINK_TIMEOUT_MS = 60_000;
14
+ /**
15
+ * Key of the global map in which Module Federation caches remote-entry load promises,
16
+ * and the separator it composes the map keys with (`SEPARATOR` in `@module-federation/sdk`).
17
+ *
18
+ * The map sits on the global root, **not** under `globalThis.__FEDERATION__` where the
19
+ * instance/share/manifest registries live: `runtime-core`'s `global.js` does
20
+ * `definePropertyGlobalVal(CurrentGlobal, "__GLOBAL_LOADING_REMOTE_ENTRY__", {})` before it
21
+ * builds `__FEDERATION__`, and `CurrentGlobal` is `globalThis`. That asymmetry is easy to
22
+ * "correct" into a silent no-op, so `federationRuntimeGlobals.test.ts` pins it against the
23
+ * real runtime.
24
+ *
25
+ * Exported for that test.
26
+ */
27
+ export const GLOBAL_LOADING_REMOTE_ENTRY = "__GLOBAL_LOADING_REMOTE_ENTRY__";
28
+ const REMOTE_ENTRY_KEY_SEPARATOR = ":";
29
+ /**
30
+ * Drops a remote entry's cached load promise so the next attempt starts from scratch.
31
+ *
32
+ * MF stores the promise returned by `getRemoteEntry` in a process-global map and — as of
33
+ * `@module-federation/runtime` 2.6.0 — only ever deletes it from `removeRemote`, never when
34
+ * the promise rejects. A single failed entry load, including one kicked off by a background
35
+ * hover preload the user never asked for, would therefore be replayed to every later caller
36
+ * and make that application unopenable for the lifetime of the page.
37
+ *
38
+ * Exported for tests.
39
+ */
40
+ export function evictRemoteEntryLoadCache(remoteInfo) {
41
+ const cache = globalThis[GLOBAL_LOADING_REMOTE_ENTRY];
42
+ if (!cache) {
43
+ return;
44
+ }
45
+ delete cache[`${remoteInfo.name}${REMOTE_ENTRY_KEY_SEPARATOR}${remoteInfo.entry}`];
46
+ }
47
+ /**
48
+ * Host-side Module Federation runtime plugin. Registered on the federation instance created
49
+ * by the remote loader; both hooks work around behaviour of the MF runtime we cannot
50
+ * configure any other way.
51
+ */
52
+ export function createHostFederationPlugin() {
53
+ return {
54
+ name: "gdc-host-remote-loader",
55
+ // Raise the hardcoded 20s preload-link timeout. Returning an object without `link`
56
+ // leaves MF's own element (and its attributes) in place — only the timeout changes.
57
+ createLink: () => ({ timeout: PRELOAD_LINK_TIMEOUT_MS }),
58
+ // Fires on both the success and the failure path of a remote-entry load; only the
59
+ // failure path needs the cache evicting.
60
+ afterLoadEntry: async ({ remoteInfo, error }) => {
61
+ if (error) {
62
+ evictRemoteEntryLoadCache(remoteInfo);
63
+ }
64
+ },
65
+ };
66
+ }
@@ -9,6 +9,19 @@ import { type IAppLifecycleCallbacks } from "../types/lifecycle.js";
9
9
  */
10
10
  export declare function registerAppLifecycleCallbacks(callbacks: IAppLifecycleCallbacks): void;
11
11
  export declare function getAppLifecycleCallbacks(): IAppLifecycleCallbacks | undefined;
12
+ /**
13
+ * Whether speculative preloading should be skipped altogether.
14
+ *
15
+ * Preloading trades bandwidth for latency, which is a bad trade when bandwidth is the scarce
16
+ * resource: on a very slow link the preload competes with the requests the page the user is
17
+ * actually looking at still needs. An explicit Save-Data opt-out is respected for the same
18
+ * reason.
19
+ *
20
+ * `navigator.connection` is not implemented everywhere; absent it we keep preloading.
21
+ *
22
+ * Exported for tests.
23
+ */
24
+ export declare function shouldSkipPreload(): boolean;
12
25
  export declare function preloadPluggableApplication(app: PluggableApplicationRegistryItem): void;
13
26
  export declare function loadPluggableApplication(app: PluggableApplicationRegistryItem): Promise<IPluggableApp>;
14
27
  //# sourceMappingURL=pluggableApplicationsLoader.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"pluggableApplicationsLoader.d.ts","sourceRoot":"","sources":["../../src/loader/pluggableApplicationsLoader.ts"],"names":[],"mappings":"AAEA,OAAO,EACH,KAAK,gCAAgC,EAIxC,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,KAAK,aAAa,EAAE,MAAM,2CAA2C,CAAC;AAG/E,OAAO,EAAE,KAAK,sBAAsB,EAAE,MAAM,uBAAuB,CAAC;AAOpE;;;;;GAKG;AACH,wBAAgB,6BAA6B,CAAC,SAAS,EAAE,sBAAsB,GAAG,IAAI,CAErF;AAED,wBAAgB,wBAAwB,IAAI,sBAAsB,GAAG,SAAS,CAE7E;AAED,wBAAgB,2BAA2B,CAAC,GAAG,EAAE,gCAAgC,GAAG,IAAI,CAwBvF;AAED,wBAAsB,wBAAwB,CAC1C,GAAG,EAAE,gCAAgC,GACtC,OAAO,CAAC,aAAa,CAAC,CAgBxB"}
1
+ {"version":3,"file":"pluggableApplicationsLoader.d.ts","sourceRoot":"","sources":["../../src/loader/pluggableApplicationsLoader.ts"],"names":[],"mappings":"AAEA,OAAO,EACH,KAAK,gCAAgC,EAIxC,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,KAAK,aAAa,EAAE,MAAM,2CAA2C,CAAC;AAG/E,OAAO,EAAE,KAAK,sBAAsB,EAAE,MAAM,uBAAuB,CAAC;AAOpE;;;;;GAKG;AACH,wBAAgB,6BAA6B,CAAC,SAAS,EAAE,sBAAsB,GAAG,IAAI,CAErF;AAED,wBAAgB,wBAAwB,IAAI,sBAAsB,GAAG,SAAS,CAE7E;AASD;;;;;;;;;;;GAWG;AACH,wBAAgB,iBAAiB,IAAI,OAAO,CAgB3C;AAED,wBAAgB,2BAA2B,CAAC,GAAG,EAAE,gCAAgC,GAAG,IAAI,CA2BvF;AAED,wBAAsB,wBAAwB,CAC1C,GAAG,EAAE,gCAAgC,GACtC,OAAO,CAAC,aAAa,CAAC,CAgBxB"}
@@ -16,7 +16,35 @@ export function registerAppLifecycleCallbacks(callbacks) {
16
16
  export function getAppLifecycleCallbacks() {
17
17
  return registeredLifecycle;
18
18
  }
19
+ const PRELOAD_HOSTILE_EFFECTIVE_TYPES = ["slow-2g", "2g"];
20
+ /**
21
+ * Whether speculative preloading should be skipped altogether.
22
+ *
23
+ * Preloading trades bandwidth for latency, which is a bad trade when bandwidth is the scarce
24
+ * resource: on a very slow link the preload competes with the requests the page the user is
25
+ * actually looking at still needs. An explicit Save-Data opt-out is respected for the same
26
+ * reason.
27
+ *
28
+ * `navigator.connection` is not implemented everywhere; absent it we keep preloading.
29
+ *
30
+ * Exported for tests.
31
+ */
32
+ export function shouldSkipPreload() {
33
+ if (typeof navigator === "undefined") {
34
+ return true;
35
+ }
36
+ const connection = navigator.connection;
37
+ if (!connection) {
38
+ return false;
39
+ }
40
+ return (connection.saveData === true ||
41
+ (connection.effectiveType !== undefined &&
42
+ PRELOAD_HOSTILE_EFFECTIVE_TYPES.includes(connection.effectiveType)));
43
+ }
19
44
  export function preloadPluggableApplication(app) {
45
+ if (shouldSkipPreload()) {
46
+ return;
47
+ }
20
48
  if (isLocalPluggableApplicationRegistryItem(app)) {
21
49
  registeredLifecycle?.onPreloadStarted?.(app.id);
22
50
  const start = now();
@@ -1 +1 @@
1
- {"version":3,"file":"remoteLoader.d.ts","sourceRoot":"","sources":["../../src/loader/remoteLoader.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,KAAK,iCAAiC,EAAE,MAAM,qBAAqB,CAAC;AAC7E,OAAO,EAAE,KAAK,aAAa,EAAE,KAAK,aAAa,EAAE,MAAM,2CAA2C,CAAC;AA4GnG,wBAAgB,8BAA8B,CAC1C,MAAM,EAAE,iCAAiC,GAC1C,OAAO,CAAC,aAAa,CAAC,CAiBxB;AA0BD,wBAAsB,sBAAsB,CACxC,MAAM,EAAE,iCAAiC,GAC1C,OAAO,CAAC,aAAa,CAAC,CAGxB;AAED,wBAAsB,iCAAiC,CACnD,MAAM,EAAE,iCAAiC,GAC1C,OAAO,CAAC,IAAI,CAAC,CAsBf"}
1
+ {"version":3,"file":"remoteLoader.d.ts","sourceRoot":"","sources":["../../src/loader/remoteLoader.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,KAAK,iCAAiC,EAAE,MAAM,qBAAqB,CAAC;AAC7E,OAAO,EAAE,KAAK,aAAa,EAAE,KAAK,aAAa,EAAE,MAAM,2CAA2C,CAAC;AAiHnG,wBAAgB,8BAA8B,CAC1C,MAAM,EAAE,iCAAiC,GAC1C,OAAO,CAAC,aAAa,CAAC,CAiBxB;AA0BD,wBAAsB,sBAAsB,CACxC,MAAM,EAAE,iCAAiC,GAC1C,OAAO,CAAC,aAAa,CAAC,CAGxB;AAED,wBAAsB,iCAAiC,CACnD,MAAM,EAAE,iCAAiC,GAC1C,OAAO,CAAC,IAAI,CAAC,CA+Bf"}
@@ -1,12 +1,17 @@
1
1
  // (C) 2026 GoodData Corporation
2
2
  import { createInstance } from "@module-federation/runtime";
3
+ import { createHostFederationPlugin } from "./federationRuntimePlugin.js";
3
4
  import { isAllowedRemoteHostname } from "./remoteUrlSecurity.js";
4
5
  const remoteAppPromises = new Map();
5
6
  const registeredRemoteScopeUrls = new Map();
6
7
  let federation;
7
8
  function getFederation() {
8
9
  if (!federation) {
9
- federation = createInstance({ name: "gdc_host", remotes: [] });
10
+ federation = createInstance({
11
+ name: "gdc_host",
12
+ remotes: [],
13
+ plugins: [createHostFederationPlugin()],
14
+ });
10
15
  }
11
16
  return federation;
12
17
  }
@@ -98,18 +103,27 @@ export async function preloadRemotePluggableApplication(remote) {
98
103
  // the page. Registration and preload run inside the try so a synchronous registration
99
104
  // failure is caught too; on any failure reject after logging, so the caller skips
100
105
  // onPreloadCompleted (matching the local and load-remote paths).
106
+ //
107
+ // "sync" — not "all" — deliberately: "all" additionally pulls every async chunk reachable
108
+ // from the expose, which for these applications is hundreds of files. Downloading that
109
+ // whole graph on hover saturated slow connections (competing with the requests the current
110
+ // page still needs), and the browser then warned "preloaded but not used" for each chunk
111
+ // the user never reached. The sync assets are what an actual mount needs first.
101
112
  try {
102
113
  registerRemote(remote);
103
114
  await getFederation().preloadRemote([
104
115
  {
105
116
  nameOrAlias: remote.scope,
106
117
  exposes: [normalizeModuleName(remote.module)],
107
- resourceCategory: "all",
118
+ resourceCategory: "sync",
108
119
  },
109
120
  ]);
110
121
  }
111
122
  catch (error) {
112
- console.error(`[host-runtime/remote-loader] Failed to preload remote "${remote.scope}/${remote.module}".`, error);
123
+ // warn, not error: preloading is an optimisation. MF counts an asset that has not
124
+ // finished within the link timeout as a failure, so a slow connection alone produces
125
+ // this — nothing is broken, the assets are simply fetched again when the app is opened.
126
+ console.warn(`[host-runtime/remote-loader] Could not preload remote "${remote.scope}/${remote.module}". This is a non-fatal warm-up failure; the application still loads on demand.`, error);
113
127
  throw error;
114
128
  }
115
129
  }
@@ -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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"}
@@ -26,15 +26,20 @@ 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
- onAppLinkClick?.({
30
- type: link.type,
31
- id: link.id,
32
- itemUrl: link.itemUrl,
33
- newTab: link.newTab,
34
- });
29
+ if (!embedded) {
30
+ const currentUrl = window.location.pathname + window.location.hash;
31
+ const areUrlsEqual = currentUrl === link.itemUrl;
32
+ if (link.newTab) {
33
+ window.open(link.itemUrl, "_blank");
34
+ }
35
+ else if (!areUrlsEqual) {
36
+ window.location.assign(link.itemUrl);
37
+ }
38
+ }
39
+ onAppLinkClick?.(link);
35
40
  link.preventDefault();
36
- return undefined;
37
- }, [onAppLinkClick]);
41
+ return link.itemUrl;
42
+ }, [onAppLinkClick, embedded]);
38
43
  const handleEvent = useCallback((event) => {
39
44
  switch (event.name) {
40
45
  case "opened":
@@ -79,5 +84,5 @@ export function GenAIChat({ workspaceId, open, onOpen, onClose, askedQuestion, a
79
84
  break;
80
85
  }
81
86
  }, [addError, addSuccess, intl, onEvent]);
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 }));
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, allowNativeLinks: false, onLinkClick: onLinkClick, onEvent: handleEvent, returnFocusTo: embedded ? EMBEDDED_AI_TRIGGER_ID : HEADER_CHAT_BUTTON_ID }));
83
88
  }
@@ -1,5 +1,6 @@
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";
3
4
  import "@gooddata/sdk-ui-gen-ai/styles/css/main.css";
4
5
  /**
5
6
  * AI-assistant open/close/toggle request. `seq` changes on every request so an identical repeat
@@ -58,6 +59,10 @@ export interface IHostChatProps {
58
59
  * visualization as an in-place overlay). Returns true if the app handled it.
59
60
  */
60
61
  onAppLinkClick?: (link: IHostChatLink) => boolean;
62
+ /**
63
+ * Delegates a chat event receive to the active application.
64
+ */
65
+ onAppEventReceive?: (event: GenAIChatEvent) => void;
61
66
  }
62
67
  /**
63
68
  * Owns and renders the host's single GenAI chat, decoupled from the host UI module.
@@ -68,5 +73,5 @@ export interface IHostChatProps {
68
73
  * (embedded/export). The header chat button and app-side controls drive it through the `visibility`
69
74
  * and `context` props; it reports its open-state and button state back via callbacks.
70
75
  */
71
- export declare function HostChat({ ctx, resolvedApplications, pathname, activeAppId, visibility, context, onOpenChange, onChatStateChange, onAppLinkClick }: IHostChatProps): import("react/jsx-runtime").JSX.Element;
76
+ export declare function HostChat({ ctx, resolvedApplications, pathname, activeAppId, visibility, context, onOpenChange, onChatStateChange, onAppLinkClick, onAppEventReceive }: IHostChatProps): import("react/jsx-runtime").JSX.Element;
72
77
  //# sourceMappingURL=HostChat.d.ts.map
@@ -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;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"}
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;AAOlF,OAAO,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAIhD,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;IAClD;;OAEG;IACH,iBAAiB,CAAC,EAAE,CAAC,KAAK,EAAE,cAAc,KAAK,IAAI,CAAC;CACvD;AAED;;;;;;;;GAQG;AACH,wBAAgB,QAAQ,CAAC,EACrB,GAAG,EACH,oBAAoB,EACpB,QAAQ,EACR,WAAW,EACX,UAAiB,EACjB,OAAc,EACd,YAAY,EACZ,iBAAiB,EACjB,cAAc,EACd,iBAAiB,EACpB,EAAE,cAAc,2CA2FhB"}
@@ -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, onAppEventReceive, }) {
22
22
  const features = useHostChromeWorkspaceFeatures(resolvedApplications, ctx, pathname);
23
23
  const shellTelemetry = useMemo(() => getAppLifecycleCallbacks()?.createTelemetryCallbacks?.("host-ui"), []);
24
24
  const chat = useHostChromeChat({
@@ -28,6 +28,7 @@ export function HostChat({ ctx, resolvedApplications, pathname, activeAppId, vis
28
28
  dialogPosition: context?.dialogPosition,
29
29
  embedded: context?.embedded,
30
30
  onAppLinkClick,
31
+ onAppEventReceive,
31
32
  });
32
33
  const { askAiAssistant: chatAskAiAssistant, openAiAssistant: chatOpenAiAssistant, open: chatOpen, close: chatClose, toggle: chatToggle, setTags: chatSetTags, setAmbientUserContext: chatSetAmbientUserContext, isOpen: chatIsOpen, showChatItem, } = chat;
33
34
  // Report the chat open-state outward so the runtime can forward it to the active pluggable
@@ -32,5 +32,5 @@ export interface IHostChromeProps {
32
32
  appPageTitle?: string;
33
33
  children?: ReactNode;
34
34
  }
35
- export declare function HostChrome({ ctx, resolvedApplications, pathname, onNavigate, onReplace: _onReplace, headerOptions, notification, showChatItem, onChatToggle, onAskAiAssistant, appPageTitle, children }: IHostChromeProps): import("react/jsx-runtime").JSX.Element;
35
+ export declare function HostChrome({ ctx, resolvedApplications, pathname, onNavigate, onReplace: _onReplace, headerOptions, notification, showChatItem, chatIsOpen, onChatToggle, onAskAiAssistant, appPageTitle, children }: IHostChromeProps): import("react/jsx-runtime").JSX.Element;
36
36
  //# sourceMappingURL=HostChrome.d.ts.map
@@ -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;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"}
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,EACH,KAAK,iBAAiB,EAEtB,KAAK,gCAAgC,EACxC,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EACH,KAAK,iBAAiB,EACtB,KAAK,mBAAmB,EACxB,KAAK,gBAAgB,EACxB,MAAM,2CAA2C,CAAC;AA6BnD,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,UAAkB,EAClB,YAAY,EACZ,gBAAgB,EAChB,YAAY,EACZ,QAAQ,EACX,EAAE,gBAAgB,2CA0NlB"}
@@ -1,20 +1,20 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  // (C) 2026 GoodData Corporation
3
3
  import { useCallback, useMemo } from "react";
4
- import { isExternalPluggableApplicationRegistryItem, } from "@gooddata/sdk-model";
5
4
  import { BackendProvider, resolveLocale } from "@gooddata/sdk-ui";
6
5
  import { AppHeaderNotifications } from "@gooddata/sdk-ui-application-header";
7
6
  import { AppHeader, DocumentHeader, ToastsCenterContextProvider, generateHeaderAccountMenuItems, generateHeaderStaticHelpMenuItems, } from "@gooddata/sdk-ui-kit";
8
7
  import { defaultHeaderTheme } from "@gooddata/sdk-ui-theme-provider";
9
8
  import defaultLogoUrl from "../assets/logo-white.svg";
10
- import { getAppLifecycleCallbacks, preloadPluggableApplication, } from "../loader/pluggableApplicationsLoader.js";
11
- import { getActiveInternalApplication, getApplicationHref } from "../loader/routing.js";
9
+ import { getAppLifecycleCallbacks } from "../loader/pluggableApplicationsLoader.js";
10
+ import { getActiveInternalApplication } from "../loader/routing.js";
12
11
  import { getBackend } from "../platformContext/backend.js";
13
12
  import { buildAppMenu, getLocalizedTitle } from "./appMenuItems.js";
14
13
  import { getUserDisplayName, isPlainLeftClick, swapWorkspaceInPath } from "./chromeHelpers.js";
15
14
  import { b, e } from "./hostChromeBem.js";
16
15
  import { HostIntlProvider } from "./HostIntlProvider.js";
17
16
  import { HostNotificationDispatcher } from "./HostNotificationDispatcher.js";
17
+ import { useAppPreloadOnHover } from "./useAppPreloadOnHover.js";
18
18
  import { useHostChromePricing } from "./useHostChromePricing.js";
19
19
  import { useHostChromeSearch } from "./useHostChromeSearch.js";
20
20
  import { useHostChromeWorkspaceFeatures } from "./useHostChromeWorkspaceFeatures.js";
@@ -37,7 +37,7 @@ const initialFaviconUrl = (typeof document !== "undefined" && document.querySele
37
37
  "/favicon.ico";
38
38
  // Switches the host scope to organization; the first organization app is then chosen by redirect logic.
39
39
  const LOGO_HREF = "/organization";
40
- export function HostChrome({ ctx, resolvedApplications, pathname, onNavigate, onReplace: _onReplace, headerOptions, notification = null, showChatItem = false, onChatToggle, onAskAiAssistant, appPageTitle, children, }) {
40
+ export function HostChrome({ ctx, resolvedApplications, pathname, onNavigate, onReplace: _onReplace, headerOptions, notification = null, showChatItem = false, chatIsOpen = false, onChatToggle, onAskAiAssistant, appPageTitle, children, }) {
41
41
  const locale = resolveLocale(ctx.preferredLocale);
42
42
  const userName = getUserDisplayName(ctx.user);
43
43
  const features = useHostChromeWorkspaceFeatures(resolvedApplications, ctx, pathname);
@@ -83,25 +83,7 @@ export function HostChrome({ ctx, resolvedApplications, pathname, onNavigate, on
83
83
  }
84
84
  }
85
85
  }, [onNavigate]);
86
- const handleHeaderMouseOver = useCallback((e) => {
87
- const anchor = e.target.closest("a[href]");
88
- if (!anchor) {
89
- return;
90
- }
91
- const href = anchor.getAttribute("href");
92
- if (!href) {
93
- return;
94
- }
95
- for (const app of resolvedApplications) {
96
- if (!isExternalPluggableApplicationRegistryItem(app)) {
97
- const appHref = getApplicationHref(app, ctx, pathname);
98
- if (appHref === href) {
99
- preloadPluggableApplication(app);
100
- return;
101
- }
102
- }
103
- }
104
- }, [resolvedApplications, ctx, pathname]);
86
+ const appPreloadOnHover = useAppPreloadOnHover(resolvedApplications, ctx, pathname);
105
87
  // Without this the anchor would do a full page load instead of a client-side navigation.
106
88
  const handleLogoClick = useCallback((e) => {
107
89
  if (!isPlainLeftClick(e)) {
@@ -153,6 +135,6 @@ export function HostChrome({ ctx, resolvedApplications, pathname, onNavigate, on
153
135
  const activeColor = ctx.theme?.header?.activeColor ?? defaultHeaderTheme.activeColor;
154
136
  const hideChrome = ctx.embeddingMode === "iframe" || ctx.isExportMode === true;
155
137
  return (_jsx(HostIntlProvider, { locale: locale, additionalMessages: appMessages, children: _jsx(BackendProvider, { backend: getBackend(), children: _jsx(ToastsCenterContextProvider, { children: _jsxs("div", { className: b(), children: [
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 })
138
+ _jsx(DocumentHeader, { pageTitle: documentPageTitle, brandTitle: documentBrandTitle, faviconUrl: faviconUrl, appleTouchIconUrl: ctx.whiteLabeling?.appleTouchIconUrl }), hideChrome ? null : (_jsx("div", { className: e("header"), onMouseOver: appPreloadOnHover.onMouseOver, onMouseLeave: appPreloadOnHover.onMouseLeave, 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, isChatOpen: chatIsOpen, 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 })
157
139
  ] }) }) }) }));
158
140
  }
@@ -2,6 +2,7 @@ import { type RefObject } from "react";
2
2
  import { type IGenAIUserContext, type PluggableApplicationRegistryItem } from "@gooddata/sdk-model";
3
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";
5
6
  export interface IPluggableApplicationRendererProps {
6
7
  app: PluggableApplicationRegistryItem;
7
8
  ctx: IPlatformContext;
@@ -30,6 +31,11 @@ export interface IPluggableApplicationRendererProps {
30
31
  itemUrl?: string;
31
32
  newTab?: boolean;
32
33
  }) => boolean) | undefined>;
34
+ /**
35
+ * Ref the renderer populates with a handler that delegates a host-chat event to the active
36
+ * app's mount handle (`onAiAssistantEventReceive`), so an embedded app can handle it in-app.
37
+ */
38
+ aiEventReceiveRef?: RefObject<((event: GenAIChatEvent) => void) | undefined>;
33
39
  /**
34
40
  * Ref the renderer populates with a handler that asks the active app's mount handle
35
41
  * (`onHostNavigationRequested`) whether the host may navigate away. Must be referentially
@@ -39,5 +45,5 @@ export interface IPluggableApplicationRendererProps {
39
45
  onHeaderChange?: (appId: string, header: IAppHeaderOptions) => void;
40
46
  onDocumentTitleChange?: (appId: string, pageTitle: string | undefined) => void;
41
47
  }
42
- export declare function PluggableApplicationRenderer({ app, ctx, pathname, aiAssistantOpen, onOpenAiAssistant, onCloseAiAssistant, onAiAssistantContext, aiLinkClickHandlerRef, navigationRequestRef, onHeaderChange, onDocumentTitleChange }: IPluggableApplicationRendererProps): import("react/jsx-runtime").JSX.Element;
48
+ export declare function PluggableApplicationRenderer({ app, ctx, pathname, aiAssistantOpen, onOpenAiAssistant, onCloseAiAssistant, onAiAssistantContext, aiLinkClickHandlerRef, aiEventReceiveRef, navigationRequestRef, onHeaderChange, onDocumentTitleChange }: IPluggableApplicationRendererProps): import("react/jsx-runtime").JSX.Element;
43
49
  //# 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,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"}
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;AAC7C,OAAO,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAkBhD,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;;;OAGG;IACH,iBAAiB,CAAC,EAAE,SAAS,CAAC,CAAC,CAAC,KAAK,EAAE,cAAc,KAAK,IAAI,CAAC,GAAG,SAAS,CAAC,CAAC;IAC7E;;;;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,iBAAiB,EACjB,oBAAoB,EACpB,cAAc,EACd,qBAAqB,EACxB,EAAE,kCAAkC,2CA4SpC"}
@@ -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, navigationRequestRef, onHeaderChange, onDocumentTitleChange, }) {
23
+ export function PluggableApplicationRenderer({ app, ctx, pathname, aiAssistantOpen, onOpenAiAssistant, onCloseAiAssistant, onAiAssistantContext, aiLinkClickHandlerRef, aiEventReceiveRef, navigationRequestRef, onHeaderChange, onDocumentTitleChange, }) {
24
24
  const intl = useIntl();
25
25
  const intlRef = useAutoupdateRef(intl);
26
26
  const ctxRef = useAutoupdateRef(ctx);
@@ -227,6 +227,19 @@ export function PluggableApplicationRenderer({ app, ctx, pathname, aiAssistantOp
227
227
  ref.current = undefined;
228
228
  };
229
229
  }, [aiLinkClickHandlerRef]);
230
+ // Expose a host-chat event delegate that defers to the active app's mount handle, so an
231
+ // embedded app can handle events in-app. Read through the ref at call time so it
232
+ // always targets the currently mounted app.
233
+ useEffect(() => {
234
+ const ref = aiEventReceiveRef;
235
+ if (!ref) {
236
+ return;
237
+ }
238
+ ref.current = (event) => mountHandleRef.current?.onAiAssistantEventReceive?.(event);
239
+ return () => {
240
+ ref.current = undefined;
241
+ };
242
+ }, [aiEventReceiveRef]);
230
243
  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: [
231
244
  _jsx("h2", { children: _jsx(FormattedMessage, { id: "gs.host.error.applicationFailedToLoad" }) }), _jsx("p", { children: viewState.message })
232
245
  ] })) : 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: {
@@ -0,0 +1,29 @@
1
+ import { type MouseEvent } from "react";
2
+ import { type PluggableApplicationRegistryItem } from "@gooddata/sdk-model";
3
+ import { type IPlatformContext } from "@gooddata/sdk-pluggable-application-model";
4
+ /**
5
+ * How long the pointer has to rest on an application link before its bundle is preloaded.
6
+ *
7
+ * The handler is attached to the header wrapper, so a pointer swept across the navigation
8
+ * passes over every link. Firing immediately would preload every application at once —
9
+ * hundreds of requests for applications the user is not going to open. Waiting for the
10
+ * pointer to settle turns "crossed the link" into "aimed at the link".
11
+ */
12
+ export declare const PRELOAD_HOVER_INTENT_MS = 150;
13
+ /**
14
+ * @internal
15
+ */
16
+ export interface IAppPreloadOnHoverHandlers {
17
+ onMouseOver: (event: MouseEvent<HTMLElement>) => void;
18
+ onMouseLeave: () => void;
19
+ }
20
+ /**
21
+ * Preloads an application's bundle when the pointer settles on its header link.
22
+ *
23
+ * Each application is scheduled at most once — the preload itself is idempotent, but there is
24
+ * no point re-arming a timer for a bundle already requested.
25
+ *
26
+ * @internal
27
+ */
28
+ export declare function useAppPreloadOnHover(resolvedApplications: PluggableApplicationRegistryItem[], ctx: IPlatformContext, pathname: string): IAppPreloadOnHoverHandlers;
29
+ //# sourceMappingURL=useAppPreloadOnHover.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useAppPreloadOnHover.d.ts","sourceRoot":"","sources":["../../src/ui/useAppPreloadOnHover.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,UAAU,EAAkC,MAAM,OAAO,CAAC;AAExE,OAAO,EAEH,KAAK,gCAAgC,EACxC,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,KAAK,gBAAgB,EAAE,MAAM,2CAA2C,CAAC;AAKlF;;;;;;;GAOG;AACH,eAAO,MAAM,uBAAuB,MAAM,CAAC;AAE3C;;GAEG;AACH,MAAM,WAAW,0BAA0B;IACvC,WAAW,EAAE,CAAC,KAAK,EAAE,UAAU,CAAC,WAAW,CAAC,KAAK,IAAI,CAAC;IACtD,YAAY,EAAE,MAAM,IAAI,CAAC;CAC5B;AAED;;;;;;;GAOG;AACH,wBAAgB,oBAAoB,CAChC,oBAAoB,EAAE,gCAAgC,EAAE,EACxD,GAAG,EAAE,gBAAgB,EACrB,QAAQ,EAAE,MAAM,GACjB,0BAA0B,CAsD5B"}
@@ -0,0 +1,69 @@
1
+ // (C) 2026 GoodData Corporation
2
+ import { useCallback, useEffect, useRef } from "react";
3
+ import { isExternalPluggableApplicationRegistryItem, } from "@gooddata/sdk-model";
4
+ import { preloadPluggableApplication } from "../loader/pluggableApplicationsLoader.js";
5
+ import { getApplicationHref } from "../loader/routing.js";
6
+ /**
7
+ * How long the pointer has to rest on an application link before its bundle is preloaded.
8
+ *
9
+ * The handler is attached to the header wrapper, so a pointer swept across the navigation
10
+ * passes over every link. Firing immediately would preload every application at once —
11
+ * hundreds of requests for applications the user is not going to open. Waiting for the
12
+ * pointer to settle turns "crossed the link" into "aimed at the link".
13
+ */
14
+ export const PRELOAD_HOVER_INTENT_MS = 150;
15
+ /**
16
+ * Preloads an application's bundle when the pointer settles on its header link.
17
+ *
18
+ * Each application is scheduled at most once — the preload itself is idempotent, but there is
19
+ * no point re-arming a timer for a bundle already requested.
20
+ *
21
+ * @internal
22
+ */
23
+ export function useAppPreloadOnHover(resolvedApplications, ctx, pathname) {
24
+ const timeoutRef = useRef(undefined);
25
+ const scheduledAppIdsRef = useRef(new Set());
26
+ const cancelPendingPreload = useCallback(() => {
27
+ if (timeoutRef.current !== undefined) {
28
+ window.clearTimeout(timeoutRef.current);
29
+ timeoutRef.current = undefined;
30
+ }
31
+ }, []);
32
+ // Do not leave a timer behind that would preload into an unmounted chrome.
33
+ useEffect(() => cancelPendingPreload, [cancelPendingPreload]);
34
+ const onMouseOver = useCallback((event) => {
35
+ // Any movement inside the header invalidates a pending preload: the pointer has
36
+ // left the link it was aimed at, so it never settled there. Cancelling up front
37
+ // covers every way out of this handler — a non-anchor target such as the workspace
38
+ // picker, an anchor belonging to no application, and an external application —
39
+ // whereas the header's own onMouseLeave only fires once the pointer is out of the
40
+ // header entirely. Re-armed below if the pointer landed on an application link.
41
+ cancelPendingPreload();
42
+ const anchor = event.target.closest("a[href]");
43
+ if (!anchor) {
44
+ return;
45
+ }
46
+ const href = anchor.getAttribute("href");
47
+ if (!href) {
48
+ return;
49
+ }
50
+ for (const app of resolvedApplications) {
51
+ if (isExternalPluggableApplicationRegistryItem(app)) {
52
+ continue;
53
+ }
54
+ if (getApplicationHref(app, ctx, pathname) !== href) {
55
+ continue;
56
+ }
57
+ if (scheduledAppIdsRef.current.has(app.id)) {
58
+ return;
59
+ }
60
+ timeoutRef.current = window.setTimeout(() => {
61
+ timeoutRef.current = undefined;
62
+ scheduledAppIdsRef.current.add(app.id);
63
+ preloadPluggableApplication(app);
64
+ }, PRELOAD_HOVER_INTENT_MS);
65
+ return;
66
+ }
67
+ }, [resolvedApplications, ctx, pathname, cancelPendingPreload]);
68
+ return { onMouseOver, onMouseLeave: cancelPendingPreload };
69
+ }
@@ -1,6 +1,7 @@
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";
4
5
  import { type IHostChromeWorkspaceFeatures } from "./useHostChromeWorkspaceFeatures.js";
5
6
  export interface IHostChromeChat {
6
7
  /** The `<GenAIChat>` element to mount, or `null` when chat is gated off. */
@@ -54,6 +55,8 @@ export interface IUseHostChromeChatArgs {
54
55
  itemUrl?: string;
55
56
  newTab?: boolean;
56
57
  }) => boolean;
58
+ /** Delegates a chat event receive to the active app. */
59
+ onAppEventReceive?: (event: GenAIChatEvent) => void;
57
60
  }
58
61
  /**
59
62
  * Owns the GenAI chat state and renders the `<GenAIChat>` element for the host chrome.
@@ -62,5 +65,5 @@ export interface IUseHostChromeChatArgs {
62
65
  * points, the runtime availability probe (`useGenAiChatAvailability`) and forwarding chat
63
66
  * events to the host telemetry callbacks.
64
67
  */
65
- export declare function useHostChromeChat({ features, ctx, telemetry, dialogPosition, embedded, onAppLinkClick }: IUseHostChromeChatArgs): IHostChromeChat;
68
+ export declare function useHostChromeChat({ features, ctx, telemetry, dialogPosition, embedded, onAppLinkClick, onAppEventReceive }: IUseHostChromeChatArgs): IHostChromeChat;
66
69
  //# 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;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"}
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;AAKnD,OAAO,EAAa,KAAK,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAChE,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;IACvG,wDAAwD;IACxD,iBAAiB,CAAC,EAAE,CAAC,KAAK,EAAE,cAAc,KAAK,IAAI,CAAC;CACvD;AAED;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAAC,EAC9B,QAAQ,EACR,GAAG,EACH,SAAS,EACT,cAAc,EACd,QAAQ,EACR,cAAc,EACd,iBAAiB,EACpB,EAAE,sBAAsB,GAAG,eAAe,CAyH1C"}
@@ -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, onAppEventReceive, }) {
15
15
  const [isChatOpen, setIsChatOpen] = useState(false);
16
16
  const [askedQuestion, setAskedQuestion] = useState(null);
17
17
  const [appendToChat, setAppendToChat] = useState(false);
@@ -61,7 +61,8 @@ 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
- }, [telemetry]);
64
+ onAppEventReceive?.(event);
65
+ }, [telemetry, onAppEventReceive]);
65
66
  const showChatItem = useGenAiChatAvailability(getBackend(), features.workspaceId, features.showChat, features.canManageProject);
66
67
  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;
67
68
  return {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gooddata/sdk-ui-pluggable-host",
3
- "version": "11.52.0-alpha.4",
3
+ "version": "11.52.0-alpha.6",
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.52.0-alpha.4",
33
- "@gooddata/sdk-backend-tiger": "11.52.0-alpha.4",
34
- "@gooddata/sdk-backend-spi": "11.52.0-alpha.4",
35
- "@gooddata/sdk-embedding": "11.52.0-alpha.4",
36
- "@gooddata/sdk-model": "11.52.0-alpha.4",
37
- "@gooddata/sdk-pluggable-application-model": "11.52.0-alpha.4",
38
- "@gooddata/sdk-ui-application-header": "11.52.0-alpha.4",
39
- "@gooddata/sdk-ui": "11.52.0-alpha.4",
40
- "@gooddata/sdk-ui-ext": "11.52.0-alpha.4",
41
- "@gooddata/sdk-ui-kit": "11.52.0-alpha.4",
42
- "@gooddata/sdk-ui-gen-ai": "11.52.0-alpha.4",
43
- "@gooddata/sdk-ui-theme-provider": "11.52.0-alpha.4",
44
- "@gooddata/sdk-ui-semantic-search": "11.52.0-alpha.4",
45
- "@gooddata/util": "11.52.0-alpha.4"
32
+ "@gooddata/sdk-backend-base": "11.52.0-alpha.6",
33
+ "@gooddata/sdk-embedding": "11.52.0-alpha.6",
34
+ "@gooddata/sdk-backend-tiger": "11.52.0-alpha.6",
35
+ "@gooddata/sdk-backend-spi": "11.52.0-alpha.6",
36
+ "@gooddata/sdk-model": "11.52.0-alpha.6",
37
+ "@gooddata/sdk-pluggable-application-model": "11.52.0-alpha.6",
38
+ "@gooddata/sdk-ui": "11.52.0-alpha.6",
39
+ "@gooddata/sdk-ui-application-header": "11.52.0-alpha.6",
40
+ "@gooddata/sdk-ui-ext": "11.52.0-alpha.6",
41
+ "@gooddata/sdk-ui-gen-ai": "11.52.0-alpha.6",
42
+ "@gooddata/sdk-ui-semantic-search": "11.52.0-alpha.6",
43
+ "@gooddata/sdk-ui-kit": "11.52.0-alpha.6",
44
+ "@gooddata/sdk-ui-theme-provider": "11.52.0-alpha.6",
45
+ "@gooddata/util": "11.52.0-alpha.6"
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.52.0-alpha.4",
89
- "@gooddata/oxlint-config": "11.52.0-alpha.4"
88
+ "@gooddata/eslint-config": "11.52.0-alpha.6",
89
+ "@gooddata/oxlint-config": "11.52.0-alpha.6"
90
90
  },
91
91
  "peerDependencies": {
92
92
  "react": ">=18.3.1",