@gooddata/sdk-ui-gen-ai 11.40.0-alpha.4 → 11.40.0-alpha.5

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.
Files changed (42) hide show
  1. package/NOTICE +3 -3
  2. package/esm/components/GenAIChat.d.ts +2 -59
  3. package/esm/components/GenAIChat.js +3 -19
  4. package/esm/components/GenAIChatConversations.d.ts +10 -7
  5. package/esm/components/GenAIChatConversations.js +31 -23
  6. package/esm/components/GenAIChatDialog.d.ts +3 -16
  7. package/esm/components/GenAIChatDialog.js +5 -15
  8. package/esm/components/GenAIChatOverlay.d.ts +10 -3
  9. package/esm/components/GenAIChatOverlay.js +19 -2
  10. package/esm/components/GenAiConversations.d.ts +26 -0
  11. package/esm/components/GenAiConversations.js +50 -0
  12. package/esm/components/GenAiStore.d.ts +75 -0
  13. package/esm/components/GenAiStore.js +62 -0
  14. package/esm/components/hooks/useThreadLoading.js +6 -2
  15. package/esm/components/messages/components/SemanticSearchTreeView.js +5 -1
  16. package/esm/components/messages/contents/CustomHyperlink.js +7 -4
  17. package/esm/components/messages/contents/VisualizationContents.js +5 -4
  18. package/esm/components/messages/conversationContents/ConversationVisualizationContent.js +6 -5
  19. package/esm/index.d.ts +3 -1
  20. package/esm/index.js +3 -1
  21. package/esm/sdk-ui-gen-ai.d.ts +153 -63
  22. package/esm/store/events.d.ts +54 -14
  23. package/esm/store/events.js +26 -5
  24. package/esm/store/messages/messagesSlice.d.ts +8 -8
  25. package/esm/store/messages/messagesSlice.js +14 -14
  26. package/esm/store/sideEffects/onConversationDelete.d.ts +2 -2
  27. package/esm/store/sideEffects/onConversationDelete.js +3 -3
  28. package/esm/store/sideEffects/onConversationPin.d.ts +4 -3
  29. package/esm/store/sideEffects/onConversationPin.js +4 -4
  30. package/esm/store/sideEffects/onConversationRename.d.ts +4 -3
  31. package/esm/store/sideEffects/onConversationRename.js +3 -3
  32. package/esm/store/sideEffects/onEvent.js +52 -11
  33. package/esm/store/sideEffects/onVisualizationSuccessSave.d.ts +2 -2
  34. package/esm/store/sideEffects/onVisualizationSuccessSave.js +5 -2
  35. package/esm/utils.d.ts +2 -2
  36. package/esm/utils.js +6 -4
  37. package/package.json +20 -20
  38. package/styles/css/conversations.css +11 -0
  39. package/styles/css/conversations.css.map +1 -1
  40. package/styles/css/main.css +11 -0
  41. package/styles/css/main.css.map +1 -1
  42. package/styles/scss/conversations.scss +12 -0
package/NOTICE CHANGED
@@ -7,9 +7,9 @@
7
7
 
8
8
  The following 3rd-party software packages may be used by or distributed with gooddata-ui-sdk. Any information relevant to third-party vendors listed below are collected using common, reasonable means.
9
9
 
10
- Date generated: 2026-6-1
10
+ Date generated: 2026-6-2
11
11
 
12
- Revision ID: 58bde4b356bb7df1b4f63c7ebd5ae8a4fee89f71
12
+ Revision ID: 9578d3fcaf586a5a43aa0d269f308f27a7f322ef
13
13
 
14
14
  ================================================================================
15
15
  ================================================================================
@@ -38471,4 +38471,4 @@ POSSIBILITY OF SUCH DAMAGE.
38471
38471
  --------------------------------------------------------------------------------
38472
38472
  --------------------------------------------------------------------------------
38473
38473
 
38474
- Report Generated by FOSSA on 2026-6-1
38474
+ Report Generated by FOSSA on 2026-6-2
@@ -1,41 +1,15 @@
1
1
  import { type ComponentType } from "react";
2
- import { type IAnalyticalBackend, type IUserWorkspaceSettings } from "@gooddata/sdk-backend-spi";
3
- import { type CatalogItem, type GenAIObjectType, type IColorPalette } from "@gooddata/sdk-model";
4
- import { type ChatEventHandler } from "../store/events.js";
5
2
  import { type LinkHandlerEvent } from "./ConfigContext.js";
3
+ import { type GenAiStoreProps } from "./GenAiStore.js";
6
4
  /**
7
5
  * Properties for the GenAIAssistant component.
8
6
  * @public
9
7
  */
10
- export type GenAIAssistantProps = {
11
- /**
12
- * Analytical backend to use for server communication.
13
- */
14
- backend?: IAnalyticalBackend;
15
- /**
16
- * The workspace ID the user is working with.
17
- */
18
- workspace?: string;
8
+ export type GenAIAssistantProps = Omit<GenAiStoreProps, "children"> & {
19
9
  /**
20
10
  * The locale to use for the chat UI.
21
11
  */
22
12
  locale?: string;
23
- /**
24
- * Color palette to use for the chat UI.
25
- */
26
- colorPalette?: IColorPalette;
27
- /**
28
- * Catalog items for autocomplete.
29
- */
30
- catalogItems?: CatalogItem[];
31
- /**
32
- * User settings to use for the chat UI.
33
- */
34
- settings?: IUserWorkspaceSettings;
35
- /**
36
- * Event handlers to subscribe to chat events.
37
- */
38
- eventHandlers?: ChatEventHandler[];
39
13
  /**
40
14
  * When provided, references to the metadata objects will be rendered as clickable links.
41
15
  * Otherwise, the metadata objects will be rendered as plain text (using object title).
@@ -59,23 +33,6 @@ export type GenAIAssistantProps = {
59
33
  * This will disable full control permissions for the user even if the user has them defined.
60
34
  */
61
35
  disableFullControl?: boolean;
62
- /**
63
- * A list of object types to search for.
64
- */
65
- objectTypes?: GenAIObjectType[];
66
- /**
67
- * Only objects with these tags will be included
68
- */
69
- includeTags?: string[];
70
- /**
71
- * Objects with these tags will be excluded
72
- */
73
- excludeTags?: string[];
74
- /**
75
- * When provided, the function will be called with the store dispatch function
76
- * after the store has been initialized.
77
- */
78
- onDispatcher?: (dispatch: (action: unknown) => void) => void;
79
36
  /**
80
37
  * Custom React node rendered when no conversation exists yet.
81
38
  */
@@ -88,20 +45,6 @@ export type GenAIAssistantProps = {
88
45
  * Additional class name applied to the root element.
89
46
  */
90
47
  className?: string;
91
- /**
92
- * When `true`, the assistant operates against the caller's preview agent
93
- * for this workspace (backend agent id: `{userId}-{workspaceId}-preview`).
94
- * New conversations are created as preview conversations and the
95
- * conversation list is filtered to preview conversations only.
96
- *
97
- * The preview agent must already exist and be enabled; otherwise
98
- * conversation creation will fail.
99
- *
100
- * Note: toggling this prop rebuilds the chat state from scratch.
101
- *
102
- * @internal
103
- */
104
- isPreview?: boolean;
105
48
  };
106
49
  /**
107
50
  * Properties for the GenAIChat component.
@@ -1,37 +1,21 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
- // (C) 2024-2026 GoodData Corporation
3
- import { useEffect } from "react";
4
- import { Provider as StoreProvider } from "react-redux";
5
2
  import { BackendProvider, WorkspaceProvider, useBackendStrict, useWorkspaceStrict } from "@gooddata/sdk-ui";
6
- import { useGenAIStore } from "../hooks/useGenAIStore.js";
7
3
  import { IntlWrapper } from "../localization/IntlWrapper.js";
8
4
  import { PermissionsProvider } from "../permissions/PermissionsContext.js";
9
5
  import { usePermissions } from "../permissions/usePermissions.js";
10
6
  import { ConfigProvider } from "./ConfigContext.js";
11
7
  import { CustomizationProvider } from "./CustomizationProvider.js";
12
8
  import { GenAIChatWrapper } from "./GenAIChatWrapper.js";
9
+ import { GenAiStore } from "./GenAiStore.js";
13
10
  /**
14
11
  * UI component that renders the Gen AI assistant.
15
12
  * @public
16
13
  */
17
14
  export function GenAIAssistant(props) {
18
- const { backend, workspace, locale, colorPalette, eventHandlers, settings, objectTypes, catalogItems, includeTags, excludeTags, onDispatcher, isPreview, } = props;
15
+ const { backend, workspace, locale, colorPalette, eventHandlers, settings, objectTypes, catalogItems, includeTags, excludeTags, isPreview, onDispatcher, } = props;
19
16
  const effectiveBackend = useBackendStrict(backend);
20
17
  const effectiveWorkspace = useWorkspaceStrict(workspace);
21
- const genAIStore = useGenAIStore(effectiveBackend, effectiveWorkspace, {
22
- colorPalette,
23
- eventHandlers,
24
- settings,
25
- objectTypes,
26
- includeTags,
27
- excludeTags,
28
- catalogItems,
29
- isPreview,
30
- });
31
- useEffect(() => {
32
- onDispatcher?.(genAIStore.dispatch);
33
- }, [genAIStore, onDispatcher]);
34
- return (_jsx(IntlWrapper, { locale: locale, children: _jsx(StoreProvider, { store: genAIStore, children: _jsx(BackendProvider, { backend: effectiveBackend, children: _jsx(WorkspaceProvider, { workspace: effectiveWorkspace, children: _jsx(PermissionsProvider, { children: _jsx(GenAIContent, { ...props }) }) }) }) }) }));
18
+ return (_jsx(IntlWrapper, { locale: locale, children: _jsx(GenAiStore, { backend: effectiveBackend, workspace: effectiveWorkspace, onDispatcher: onDispatcher, colorPalette: colorPalette, eventHandlers: eventHandlers, settings: settings, objectTypes: objectTypes, includeTags: includeTags, excludeTags: excludeTags, catalogItems: catalogItems, isPreview: isPreview, children: _jsx(BackendProvider, { backend: effectiveBackend, children: _jsx(WorkspaceProvider, { workspace: effectiveWorkspace, children: _jsx(PermissionsProvider, { children: _jsx(GenAIContent, { ...props }) }) }) }) }) }));
35
19
  }
36
20
  /**
37
21
  * UI component that renders the Gen AI chat.
@@ -1,18 +1,21 @@
1
- import { type FC } from "react";
2
- import { setHistoryAction } from "../store/chatWindow/chatWindowSlice.js";
1
+ import { type FC, type ReactElement } from "react";
2
+ import { type IChatConversationLocal } from "../model.js";
3
3
  import { conversationSelector, conversationsSelector } from "../store/messages/messagesSelectors.js";
4
- import { deleteConversationAction, pinConversationAction, renameConversationAction, setCurrentConversationAction } from "../store/messages/messagesSlice.js";
4
+ import { deleteConversationAction, pinConversationAction, renameConversationAction } from "../store/messages/messagesSlice.js";
5
5
  type GenAIChatConversationsStateProps = {
6
6
  conversation: ReturnType<typeof conversationSelector>;
7
7
  conversations: ReturnType<typeof conversationsSelector>;
8
8
  };
9
9
  type GenAIChatConversationsDispatchProps = {
10
- setHistory: typeof setHistoryAction;
11
- loadConversation: typeof setCurrentConversationAction;
12
10
  deleteConversation: typeof deleteConversationAction;
13
11
  pinConversation: typeof pinConversationAction;
14
12
  renameConversation: typeof renameConversationAction;
15
13
  };
16
- export type GenAIChatConversationsProps = GenAIChatConversationsStateProps & GenAIChatConversationsDispatchProps;
17
- export declare const GenAIChatConversations: FC;
14
+ type GenAIChatConversationsExternalProps = {
15
+ wrapper?: ReactElement;
16
+ onClose?: () => void;
17
+ onSelect: (conversation: IChatConversationLocal) => void;
18
+ };
19
+ export type GenAIChatConversationsProps = GenAIChatConversationsStateProps & GenAIChatConversationsDispatchProps & GenAIChatConversationsExternalProps;
20
+ export declare const GenAIChatConversations: FC<GenAIChatConversationsExternalProps>;
18
21
  export {};
@@ -1,14 +1,13 @@
1
1
  import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  // (C) 2026 GoodData Corporation
3
- import { useCallback, useEffect, useMemo, useRef, useState, } from "react";
3
+ import { cloneElement, useCallback, useEffect, useMemo, useRef, useState, } from "react";
4
4
  import cx from "classnames";
5
5
  import { FormattedMessage, useIntl } from "react-intl";
6
6
  import { connect, useSelector } from "react-redux";
7
- import { DefaultUiMenuInteractiveItemWrapper, Dropdown, UiDrawer, UiIcon, UiIconButton, UiMenu, } from "@gooddata/sdk-ui-kit";
7
+ import { DefaultUiMenuInteractiveItemWrapper, Dropdown, UiDrawer, UiIcon, UiIconButton, UiMenu, UiSkeleton, } from "@gooddata/sdk-ui-kit";
8
8
  import { catalogItemsSelector } from "../store/chatWindow/chatWindowSelectors.js";
9
- import { setHistoryAction } from "../store/chatWindow/chatWindowSlice.js";
10
- import { conversationSelector, conversationsSelector } from "../store/messages/messagesSelectors.js";
11
- import { deleteConversationAction, pinConversationAction, renameConversationAction, setCurrentConversationAction, } from "../store/messages/messagesSlice.js";
9
+ import { conversationSelector, conversationsLoadedSelector, conversationsSelector, } from "../store/messages/messagesSelectors.js";
10
+ import { deleteConversationAction, pinConversationAction, renameConversationAction, } from "../store/messages/messagesSlice.js";
12
11
  import { isConversationWithLocalId } from "../store/utils.js";
13
12
  import { generateTemporaryTitle } from "../utils.js";
14
13
  import { collectReferences, replaceReferences } from "./completion/references.js";
@@ -17,8 +16,7 @@ import { ConversationRenameDialog } from "./ConversationRenameDialog.js";
17
16
  import { useFullscreenCheck } from "./hooks/useFullscreenCheck.js";
18
17
  import { useHistoryCheck } from "./hooks/useHistoryCheck.js";
19
18
  import { ConversationDateGroup, groupConversationsByDate } from "./utils/conversationGrouper.js";
20
- function GenAIChatConversationsComponent({ setHistory, deleteConversation, pinConversation, renameConversation, loadConversation, conversations, conversation: currentConversation, }) {
21
- const ref = useRef(undefined);
19
+ function GenAIChatConversationsComponent({ onClose, onSelect, wrapper, deleteConversation, pinConversation, renameConversation, conversations, conversation: currentConversation, }) {
22
20
  const menuRef = useRef(undefined);
23
21
  const intl = useIntl();
24
22
  const { isFullscreen, isSmallScreen } = useFullscreenCheck();
@@ -29,6 +27,7 @@ function GenAIChatConversationsComponent({ setHistory, deleteConversation, pinCo
29
27
  const [draggedConversationId, setDraggedConversationId] = useState();
30
28
  const [activeDropZone, setActiveDropZone] = useState();
31
29
  const catalogItems = useSelector(catalogItemsSelector);
30
+ const conversationLoaded = useSelector(conversationsLoadedSelector);
32
31
  const groupedConversations = useMemo(() => groupConversationsByDate(conversations), [conversations]);
33
32
  const menuItems = useMemo(() => groupedConversations.map((group) => ({
34
33
  type: "group",
@@ -113,7 +112,7 @@ function GenAIChatConversationsComponent({ setHistory, deleteConversation, pinCo
113
112
  }
114
113
  else {
115
114
  pinConversation({
116
- conversationId: selectedConversation.localId,
115
+ conversation: selectedConversation,
117
116
  pinned: !selectedConversation.pinned,
118
117
  });
119
118
  }
@@ -141,12 +140,12 @@ function GenAIChatConversationsComponent({ setHistory, deleteConversation, pinCo
141
140
  menuRef.current?.focus();
142
141
  }, []);
143
142
  const handleSelect = useCallback((conversation) => {
144
- loadConversation({ conversation });
145
- setHistory({ isHistory: false });
146
- }, [loadConversation, setHistory]);
143
+ onSelect(conversation);
144
+ onClose?.();
145
+ }, [onSelect, onClose]);
147
146
  const handleDeleteSubmit = useCallback(() => {
148
147
  if (conversationToDelete) {
149
- deleteConversation({ conversationId: conversationToDelete.localId });
148
+ deleteConversation({ conversation: conversationToDelete });
150
149
  }
151
150
  setConversationToDelete(undefined);
152
151
  menuRef.current?.focus();
@@ -154,7 +153,7 @@ function GenAIChatConversationsComponent({ setHistory, deleteConversation, pinCo
154
153
  const handleRenameSubmit = useCallback((title) => {
155
154
  if (conversationToRename) {
156
155
  renameConversation({
157
- conversationId: conversationToRename.localId,
156
+ conversation: conversationToRename,
158
157
  title,
159
158
  });
160
159
  }
@@ -198,7 +197,7 @@ function GenAIChatConversationsComponent({ setHistory, deleteConversation, pinCo
198
197
  const shouldBePinned = dropZone === "pin";
199
198
  if (draggedConversation.pinned !== shouldBePinned) {
200
199
  pinConversation({
201
- conversationId: draggedConversation.localId,
200
+ conversation: draggedConversation,
202
201
  pinned: shouldBePinned,
203
202
  });
204
203
  }
@@ -212,15 +211,23 @@ function GenAIChatConversationsComponent({ setHistory, deleteConversation, pinCo
212
211
  setConversationToDelete(focusedItem.data);
213
212
  }
214
213
  }, []);
214
+ const Component = wrapper ?? _jsx(DrawerComponent, { onClose: onClose });
215
+ return (_jsxs(_Fragment, { children: [cloneElement(Component, {}, _jsx("div", { className: cx("gd-gen-ai-chat__window__conversations", {
216
+ "gd-gen-ai-chat__window__conversations--isFullscreen": isFullscreen,
217
+ "gd-gen-ai-chat__window__conversations--isSmallScreen": isSmallScreen,
218
+ }), children: _jsx(DrawerContent, { isOpen: isHistory, openedId: openedId, conversationLoaded: conversationLoaded, selectedId: currentConversation?.localId, menuRef: menuRef, menuItems: menuItems, conversations: conversations ?? [], handleSelect: handleSelect, draggedConversation: draggedConversation, activeDropZone: activeDropZone, onDragStart: handleDragStart, onDragEnd: handleDragEnd, onDropZoneDragOver: handleDropZoneDragOver, onDropZoneDragLeave: handleDropZoneDragLeave, onDropZoneDrop: handleDropZoneDrop, onMenuUnhandledKeyDown: handleMenuUnhandledKeyDown }) })), conversationToDelete ? (_jsx(ConversationDeleteDialog, { conversation: conversationToDelete, onClose: handleDeleteCancel, onDelete: handleDeleteSubmit })) : null, conversationToRename ? (_jsx(ConversationRenameDialog, { conversation: conversationToRename, onClose: handleRenameCancel, onRename: handleRenameSubmit })) : null] }));
219
+ }
220
+ function DrawerComponent({ onClose, children }) {
221
+ const ref = useRef(undefined);
222
+ const { isHistory } = useHistoryCheck();
223
+ const intl = useIntl();
215
224
  return (_jsxs(_Fragment, { children: [
216
225
  _jsx("div", { className: "gd-gen-ai-chat__window__drawer", ref: ref }), _jsx(UiDrawer, { anchor: "left", closeButtonSize: "medium", showCloseButton: true, open: isHistory, node: ref.current, showBackdrop: false, header: _jsxs("div", { style: { width: "100%" }, children: [
217
226
  _jsx("div", { className: "gd-gen-ai-chat__window__conversations__header", children: intl.formatMessage({ id: "gd.gen-ai.conversations.title" }) }), _jsx("div", { className: "gd-gen-ai-chat__window__conversations__divider" })
218
- ] }), closeLabel: intl.formatMessage({ id: "gd.gen-ai.conversations.close-label" }), onClickClose: () => setHistory({ isHistory: false }), onClickOutside: () => setHistory({ isHistory: false }), children: _jsx("div", { className: cx("gd-gen-ai-chat__window__conversations", {
219
- "gd-gen-ai-chat__window__conversations--isFullscreen": isFullscreen,
220
- "gd-gen-ai-chat__window__conversations--isSmallScreen": isSmallScreen,
221
- }), children: _jsx(DrawerContent, { isOpen: isHistory, openedId: openedId, selectedId: currentConversation?.localId, menuRef: menuRef, menuItems: menuItems, conversations: conversations ?? [], handleSelect: handleSelect, draggedConversation: draggedConversation, activeDropZone: activeDropZone, onDragStart: handleDragStart, onDragEnd: handleDragEnd, onDropZoneDragOver: handleDropZoneDragOver, onDropZoneDragLeave: handleDropZoneDragLeave, onDropZoneDrop: handleDropZoneDrop, onMenuUnhandledKeyDown: handleMenuUnhandledKeyDown }) }) }), conversationToDelete ? (_jsx(ConversationDeleteDialog, { conversation: conversationToDelete, onClose: handleDeleteCancel, onDelete: handleDeleteSubmit })) : null, conversationToRename ? (_jsx(ConversationRenameDialog, { conversation: conversationToRename, onClose: handleRenameCancel, onRename: handleRenameSubmit })) : null] }));
227
+ ] }), closeLabel: intl.formatMessage({ id: "gd.gen-ai.conversations.close-label" }), onClickClose: onClose, onClickOutside: onClose, children: children })
228
+ ] }));
222
229
  }
223
- function DrawerContent({ isOpen, openedId, selectedId, menuRef, conversations, menuItems, handleSelect, draggedConversation, activeDropZone, onDragStart, onDragEnd, onDropZoneDragOver, onDropZoneDragLeave, onDropZoneDrop, onMenuUnhandledKeyDown, }) {
230
+ function DrawerContent({ isOpen, openedId, selectedId, menuRef, conversations, menuItems, handleSelect, draggedConversation, conversationLoaded, activeDropZone, onDragStart, onDragEnd, onDropZoneDragOver, onDropZoneDragLeave, onDropZoneDrop, onMenuUnhandledKeyDown, }) {
224
231
  const intl = useIntl();
225
232
  const conversationsListRef = useRef(null);
226
233
  const pinnedItems = useMemo(() => {
@@ -230,12 +237,15 @@ function DrawerContent({ isOpen, openedId, selectedId, menuRef, conversations, m
230
237
  return menuItems.filter((item) => item.id !== ConversationDateGroup.PINNED);
231
238
  }, [menuItems]);
232
239
  useEffect(() => {
233
- if (!isOpen || !selectedId) {
240
+ if (!isOpen || !selectedId || !conversationLoaded) {
234
241
  return;
235
242
  }
236
243
  const selectedItem = conversationsListRef.current?.querySelector(`[data-conversation-id="${selectedId}"]`);
237
244
  selectedItem?.scrollIntoView({ block: "nearest" });
238
- }, [isOpen, selectedId, menuItems]);
245
+ }, [isOpen, selectedId, menuItems, conversationLoaded]);
246
+ if (!conversationLoaded) {
247
+ return _jsx(UiSkeleton, { itemsCount: 3, itemWidth: "auto", itemHeight: 20, itemsGap: 8 });
248
+ }
239
249
  return (_jsx(_Fragment, { children: (conversations ?? []).length === 0 ? (_jsxs("div", { className: "gd-gen-ai-chat__window__conversations__empty", children: [
240
250
  _jsx(UiIcon, { type: "history2", size: 20, color: "complementary-6" }), _jsx("div", { className: "gd-gen-ai-chat__window__conversations__empty__text", children: _jsx(FormattedMessage, { id: "gd.gen-ai.conversations.empty", values: {
241
251
  br: _jsx("br", {}),
@@ -328,11 +338,9 @@ const mapStateToProps = (state) => ({
328
338
  conversations: conversationsSelector(state),
329
339
  });
330
340
  const mapDispatchToProps = {
331
- setHistory: setHistoryAction,
332
341
  deleteConversation: deleteConversationAction,
333
342
  pinConversation: pinConversationAction,
334
343
  renameConversation: renameConversationAction,
335
- loadConversation: setCurrentConversationAction,
336
344
  };
337
345
  export const GenAIChatConversations = connect(mapStateToProps, mapDispatchToProps)(GenAIChatConversationsComponent);
338
346
  function getConversationGroupLabel(group, intl) {
@@ -1,29 +1,16 @@
1
1
  import { type ComponentType, type RefObject } from "react";
2
- import { type EnhancedStore } from "@reduxjs/toolkit";
3
- import { type IAnalyticalBackend, type IUserWorkspaceSettings } from "@gooddata/sdk-backend-spi";
4
- import { type CatalogItem, type GenAIObjectType, type IColorPalette } from "@gooddata/sdk-model";
5
- import { type ChatEventHandler } from "../store/events.js";
6
2
  import { type LinkHandlerEvent } from "./ConfigContext.js";
7
- export type GenAIChatDialogProps = {
8
- backend?: IAnalyticalBackend;
9
- workspace?: string;
3
+ import { type GenAiStoreProps } from "./GenAiStore.js";
4
+ export type GenAIChatDialogProps = Omit<GenAiStoreProps, "children"> & {
10
5
  isOpen: boolean;
11
6
  locale?: string;
12
7
  canManage?: boolean;
13
8
  canAnalyze?: boolean;
14
9
  canFullControl?: boolean;
15
- objectTypes?: GenAIObjectType[];
16
- includeTags?: string[];
17
- excludeTags?: string[];
18
- settings?: IUserWorkspaceSettings;
19
10
  onOpen: () => void;
20
11
  onClose: () => void;
21
- eventHandlers?: ChatEventHandler[];
22
- colorPalette?: IColorPalette;
23
- catalogItems?: CatalogItem[];
24
12
  returnFocusTo?: RefObject<HTMLElement | null> | string;
25
13
  onLinkClick?: (linkClickEvent: LinkHandlerEvent) => void;
26
- onDispatcher?: (dispatch: EnhancedStore["dispatch"]) => void;
27
14
  LandingScreenComponentProvider?: () => ComponentType;
28
15
  };
29
- export declare function GenAIChatDialog({ backend, workspace, locale, isOpen, onOpen, onClose, settings, returnFocusTo, objectTypes, includeTags, excludeTags, canManage, canAnalyze, canFullControl, eventHandlers, catalogItems, colorPalette, onLinkClick, onDispatcher, LandingScreenComponentProvider }: GenAIChatDialogProps): import("react/jsx-runtime").JSX.Element | null;
16
+ export declare function GenAIChatDialog({ backend, workspace, locale, isOpen, onOpen, onClose, settings, returnFocusTo, objectTypes, includeTags, excludeTags, canManage, canAnalyze, canFullControl, eventHandlers, catalogItems, colorPalette, onLinkClick, onDispatcher, LandingScreenComponentProvider }: GenAIChatDialogProps): import("react/jsx-runtime").JSX.Element;
@@ -1,10 +1,8 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  // (C) 2024-2026 GoodData Corporation
3
3
  import { useCallback, useEffect, useMemo, useRef } from "react";
4
- import { Provider as StoreProvider } from "react-redux";
5
4
  import { BackendProvider, WorkspaceProvider, useBackendStrict, useWorkspaceStrict } from "@gooddata/sdk-ui";
6
5
  import { OverlayController, OverlayControllerProvider, useOverlayController } from "@gooddata/sdk-ui-kit";
7
- import { useGenAIStore } from "../hooks/useGenAIStore.js";
8
6
  import { IntlWrapper } from "../localization/IntlWrapper.js";
9
7
  import { isOpenSelector } from "../store/chatWindow/chatWindowSelectors.js";
10
8
  import { setOpenAction } from "../store/chatWindow/chatWindowSlice.js";
@@ -12,6 +10,7 @@ import { getIsOpened } from "../store/localStorage.js";
12
10
  import { ConfigProvider } from "./ConfigContext.js";
13
11
  import { CustomizationProvider } from "./CustomizationProvider.js";
14
12
  import { GenAIChatOverlay } from "./GenAIChatOverlay.js";
13
+ import { GenAiStore } from "./GenAiStore.js";
15
14
  // Default z-index:
16
15
  // - High enough to be over other dialogs
17
16
  // - Below chart tooltips
@@ -19,15 +18,9 @@ const DEFAULT_CHAT_Z_INDEX = 3000;
19
18
  export function GenAIChatDialog({ backend, workspace, locale, isOpen, onOpen, onClose, settings, returnFocusTo, objectTypes, includeTags, excludeTags, canManage = false, canAnalyze = false, canFullControl = false, eventHandlers, catalogItems, colorPalette, onLinkClick, onDispatcher, LandingScreenComponentProvider, }) {
20
19
  const effectiveBackend = useBackendStrict(backend);
21
20
  const effectiveWorkspace = useWorkspaceStrict(workspace);
22
- const genAIStore = useGenAIStore(effectiveBackend, effectiveWorkspace, {
23
- eventHandlers,
24
- colorPalette,
25
- settings,
26
- objectTypes,
27
- includeTags,
28
- excludeTags,
29
- catalogItems,
30
- });
21
+ return (_jsx(IntlWrapper, { locale: locale, children: _jsx(GenAiStore, { backend: effectiveBackend, workspace: effectiveWorkspace, onDispatcher: onDispatcher, eventHandlers: eventHandlers, colorPalette: colorPalette, settings: settings, objectTypes: objectTypes, includeTags: includeTags, excludeTags: excludeTags, catalogItems: catalogItems, children: (genAIStore) => (_jsx(GenAIChatDialogContent, { genAIStore: genAIStore, backend: effectiveBackend, workspace: effectiveWorkspace, isOpen: isOpen, onOpen: onOpen, onClose: onClose, returnFocusTo: returnFocusTo, onLinkClick: onLinkClick, catalogItems: catalogItems, canManage: canManage, canAnalyze: canAnalyze, canFullControl: canFullControl, LandingScreenComponentProvider: LandingScreenComponentProvider })) }) }));
22
+ }
23
+ function GenAIChatDialogContent({ genAIStore, backend, workspace, isOpen, onOpen, onClose, returnFocusTo, onLinkClick, catalogItems, canManage, canAnalyze, canFullControl, LandingScreenComponentProvider, }) {
31
24
  const open = useRef(onOpen);
32
25
  open.current = onOpen;
33
26
  const close = useRef(onClose);
@@ -60,13 +53,10 @@ export function GenAIChatDialog({ backend, workspace, locale, isOpen, onOpen, on
60
53
  genAIStore.dispatch(setOpenAction({ isOpen: false }));
61
54
  close.current();
62
55
  }, [genAIStore]);
63
- useEffect(() => {
64
- onDispatcher?.(genAIStore.dispatch);
65
- }, [genAIStore, onDispatcher]);
66
56
  // Some apps, like Dashboards, already have an overlay controller, so we need to use that one
67
57
  const parentOverlayController = useOverlayController();
68
58
  const chatOverlayController = useMemo(() => parentOverlayController ?? OverlayController.getInstance(DEFAULT_CHAT_Z_INDEX), [parentOverlayController]);
69
59
  if (!isOpen)
70
60
  return null;
71
- return (_jsx(IntlWrapper, { locale: locale, children: _jsx(StoreProvider, { store: genAIStore, children: _jsx(BackendProvider, { backend: effectiveBackend, children: _jsx(WorkspaceProvider, { workspace: effectiveWorkspace, children: _jsx(OverlayControllerProvider, { overlayController: chatOverlayController, children: _jsx(ConfigProvider, { allowNativeLinks: true, linkHandler: onLinkClick, catalogItems: catalogItems, canManage: canManage, canAnalyze: canAnalyze, canFullControl: canFullControl, children: _jsx(CustomizationProvider, { landingScreenComponentProvider: LandingScreenComponentProvider, children: _jsx(GenAIChatOverlay, { returnFocusTo: returnFocusTo, onClose: onCloseHandler }) }) }) }) }) }) }) }));
61
+ return (_jsx(BackendProvider, { backend: backend, children: _jsx(WorkspaceProvider, { workspace: workspace, children: _jsx(OverlayControllerProvider, { overlayController: chatOverlayController, children: _jsx(ConfigProvider, { allowNativeLinks: true, linkHandler: onLinkClick, catalogItems: catalogItems, canManage: canManage, canAnalyze: canAnalyze, canFullControl: canFullControl, children: _jsx(CustomizationProvider, { landingScreenComponentProvider: LandingScreenComponentProvider, children: _jsx(GenAIChatOverlay, { returnFocusTo: returnFocusTo, onClose: onCloseHandler }) }) }) }) }) }));
72
62
  }
@@ -1,6 +1,13 @@
1
- import { type RefObject } from "react";
2
- export type GenAIChatOverlayProps = {
1
+ import { type FC, type RefObject } from "react";
2
+ import { setHistoryAction } from "../store/chatWindow/chatWindowSlice.js";
3
+ import { setCurrentConversationAction } from "../store/messages/messagesSlice.js";
4
+ export type GenAIChatOverlayDispatchProps = {
5
+ setHistory: typeof setHistoryAction;
6
+ loadConversation: typeof setCurrentConversationAction;
7
+ };
8
+ export type GenAIChatOverlayExternalProps = {
3
9
  returnFocusTo?: RefObject<HTMLElement | null> | string;
4
10
  onClose: () => void;
5
11
  };
6
- export declare function GenAIChatOverlay({ onClose, returnFocusTo }: GenAIChatOverlayProps): import("react/jsx-runtime").JSX.Element;
12
+ export type GenAIChatOverlayProps = GenAIChatOverlayExternalProps & GenAIChatOverlayDispatchProps;
13
+ export declare const GenAIChatOverlay: FC<GenAIChatOverlayExternalProps>;
@@ -1,21 +1,38 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ // (C) 2024-2026 GoodData Corporation
3
+ import { useCallback } from "react";
2
4
  import cx from "classnames";
3
5
  import { useIntl } from "react-intl";
6
+ import { connect } from "react-redux";
4
7
  import { Dialog } from "@gooddata/sdk-ui-kit";
8
+ import { setHistoryAction } from "../store/chatWindow/chatWindowSlice.js";
9
+ import { setCurrentConversationAction } from "../store/messages/messagesSlice.js";
5
10
  import { GenAIChatConversations } from "./GenAIChatConversations.js";
6
11
  import { GenAIChatHeader } from "./GenAIChatHeader.js";
7
12
  import { GenAIChatWrapper } from "./GenAIChatWrapper.js";
8
13
  import { useFullscreenCheck } from "./hooks/useFullscreenCheck.js";
9
- export function GenAIChatOverlay({ onClose, returnFocusTo }) {
14
+ function GenAIChatOverlayComponent({ onClose, returnFocusTo, setHistory, loadConversation, }) {
10
15
  const intl = useIntl();
11
16
  const { isFullscreen } = useFullscreenCheck();
12
17
  const classNames = cx("gd-gen-ai-chat__window", {
13
18
  "gd-gen-ai-chat__window--fullscreen": isFullscreen,
14
19
  });
20
+ const onHistoryClose = useCallback(() => {
21
+ setHistory({ isHistory: false });
22
+ }, [setHistory]);
23
+ const onSelectConversation = useCallback((conversation) => {
24
+ loadConversation({ conversation });
25
+ setHistory({ isHistory: false });
26
+ }, [loadConversation, setHistory]);
15
27
  return (_jsxs(Dialog, { isModal: isFullscreen, returnFocusTo: returnFocusTo, returnFocusAfterClose: !!returnFocusTo, alignPoints: [{ align: isFullscreen ? "cc cc" : "br br" }], submitOnEnterKey: false, closeOnEscape: true, closeOnParentScroll: false, closeOnMouseDrag: false, onClose: onClose, className: classNames, accessibilityConfig: {
16
28
  title: intl.formatMessage({ id: "gd.gen-ai.dialog.label" }),
17
29
  isModal: isFullscreen,
18
30
  }, children: [
19
- _jsx(GenAIChatHeader, { onClose: onClose }), _jsx(GenAIChatConversations, {}), _jsx(GenAIChatWrapper, { autofocus: true })
31
+ _jsx(GenAIChatHeader, { onClose: onClose }), _jsx(GenAIChatConversations, { onClose: onHistoryClose, onSelect: onSelectConversation }), _jsx(GenAIChatWrapper, { autofocus: true })
20
32
  ] }));
21
33
  }
34
+ const mapDispatchToProps = {
35
+ setHistory: setHistoryAction,
36
+ loadConversation: setCurrentConversationAction,
37
+ };
38
+ export const GenAIChatOverlay = connect(null, mapDispatchToProps)(GenAIChatOverlayComponent);
@@ -0,0 +1,26 @@
1
+ import { type IChatConversationLocal } from "../model.js";
2
+ import { type GenAiStoreProps } from "./GenAiStore.js";
3
+ /**
4
+ * Properties for the GenAIConversations component.
5
+ * @public
6
+ */
7
+ export type GenAIConversationsProps = Omit<GenAiStoreProps, "children"> & {
8
+ /**
9
+ * The locale to use for the chat UI.
10
+ */
11
+ locale?: string;
12
+ /**
13
+ * When provided, the function will be called when a conversation is selected.
14
+ * The function will be called with the selected conversation.
15
+ */
16
+ onConversationSelect?: (conversation: IChatConversationLocal) => void;
17
+ /**
18
+ * Additional class name applied to the root element.
19
+ */
20
+ className?: string;
21
+ };
22
+ /**
23
+ * UI component that renders the Gen AI assistant.
24
+ * @public
25
+ */
26
+ export declare function GenAIConversations(props: GenAIConversationsProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,50 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ // (C) 2024-2026 GoodData Corporation
3
+ import { useCallback } from "react";
4
+ import cx from "classnames";
5
+ import noop from "lodash-es/noop.js";
6
+ import { connect } from "react-redux";
7
+ import { BackendProvider, WorkspaceProvider, useBackendStrict, useWorkspaceStrict } from "@gooddata/sdk-ui";
8
+ import { IntlWrapper } from "../localization/IntlWrapper.js";
9
+ import { PermissionsProvider } from "../permissions/PermissionsContext.js";
10
+ import { usePermissions } from "../permissions/usePermissions.js";
11
+ import { settingsSelector } from "../store/chatWindow/chatWindowSelectors.js";
12
+ import { cancelAsyncAction, loadThreadAction, setCurrentConversationAction, } from "../store/messages/messagesSlice.js";
13
+ import { ConfigProvider } from "./ConfigContext.js";
14
+ import { CustomizationProvider } from "./CustomizationProvider.js";
15
+ import { GenAIChatConversations } from "./GenAIChatConversations.js";
16
+ import { GenAiStore } from "./GenAiStore.js";
17
+ import { useThreadLoading } from "./hooks/useThreadLoading.js";
18
+ /**
19
+ * UI component that renders the Gen AI assistant.
20
+ * @public
21
+ */
22
+ export function GenAIConversations(props) {
23
+ const { backend, workspace, locale, eventHandlers, settings, catalogItems, onDispatcher, isPreview, objectTypes, includeTags, excludeTags, colorPalette, } = props;
24
+ const effectiveBackend = useBackendStrict(backend);
25
+ const effectiveWorkspace = useWorkspaceStrict(workspace);
26
+ return (_jsx(IntlWrapper, { locale: locale, children: _jsx(GenAiStore, { backend: effectiveBackend, workspace: effectiveWorkspace, onDispatcher: onDispatcher, eventHandlers: eventHandlers, settings: settings, catalogItems: catalogItems, isPreview: isPreview, excludeTags: excludeTags, includeTags: includeTags, colorPalette: colorPalette, objectTypes: objectTypes, children: _jsx(BackendProvider, { backend: effectiveBackend, children: _jsx(WorkspaceProvider, { workspace: effectiveWorkspace, children: _jsx(PermissionsProvider, { children: _jsx(GenAIConversationsContent, { ...props }) }) }) }) }) }));
27
+ }
28
+ const mapDispatchToProps = {
29
+ loadConversation: setCurrentConversationAction,
30
+ loadThread: loadThreadAction,
31
+ cancelLoading: cancelAsyncAction,
32
+ };
33
+ const mapStateToProps = (state) => ({
34
+ settings: settingsSelector(state),
35
+ });
36
+ const GenAIConversationsContent = connect(mapStateToProps, mapDispatchToProps)(function GenAIConversationsContent(props) {
37
+ const { catalogItems, className, onConversationSelect = noop, loadThread, cancelLoading, loadConversation, settings, } = props;
38
+ const { loading } = usePermissions();
39
+ const classNames = cx("gd-gen-ai-chat__embed__conversations", className);
40
+ useThreadLoading({
41
+ initializing: loading || !settings,
42
+ loadThread,
43
+ cancelLoading,
44
+ });
45
+ const onSelectConversation = useCallback((conversation) => {
46
+ loadConversation({ conversation });
47
+ onConversationSelect(conversation);
48
+ }, [loadConversation, onConversationSelect]);
49
+ return (_jsx(ConfigProvider, { catalogItems: catalogItems, allowNativeLinks: false, canFullControl: false, canManage: false, canAnalyze: false, children: _jsx(CustomizationProvider, { children: _jsx(GenAIChatConversations, { wrapper: _jsx("div", { className: classNames }), onSelect: onSelectConversation }) }) }));
50
+ });
@@ -0,0 +1,75 @@
1
+ import { type ReactNode } from "react";
2
+ import { type EnhancedStore } from "@reduxjs/toolkit";
3
+ import { type IAnalyticalBackend, type IUserWorkspaceSettings } from "@gooddata/sdk-backend-spi";
4
+ import { type CatalogItem, type GenAIObjectType, type IColorPalette } from "@gooddata/sdk-model";
5
+ import { type ChatEventHandler } from "../store/events.js";
6
+ /**
7
+ * Props for the GenAiStore component.
8
+ * @public
9
+ */
10
+ export type GenAiStoreProps = {
11
+ /**
12
+ * Analytical backend to use for server communication.
13
+ */
14
+ backend?: IAnalyticalBackend;
15
+ /**
16
+ * The workspace ID the user is working with.
17
+ */
18
+ workspace?: string;
19
+ /**
20
+ * Catalog items for autocomplete.
21
+ */
22
+ catalogItems?: CatalogItem[];
23
+ /**
24
+ * User settings to use for the chat UI.
25
+ */
26
+ settings?: IUserWorkspaceSettings;
27
+ /**
28
+ * Event handlers to subscribe to chat events.
29
+ */
30
+ eventHandlers?: ChatEventHandler[];
31
+ /**
32
+ * A list of object types to search for.
33
+ */
34
+ objectTypes?: GenAIObjectType[];
35
+ /**
36
+ * Only objects with these tags will be included
37
+ */
38
+ includeTags?: string[];
39
+ /**
40
+ * Objects with these tags will be excluded
41
+ */
42
+ excludeTags?: string[];
43
+ /**
44
+ * Color palette to use for the chat UI.
45
+ */
46
+ colorPalette?: IColorPalette;
47
+ /**
48
+ * When provided, the function will be called with the store dispatch function
49
+ * after the store has been initialized.
50
+ */
51
+ onDispatcher?: (dispatch: EnhancedStore["dispatch"]) => void;
52
+ /**
53
+ * Render function or component to be wrapped with the Gen AI store.
54
+ */
55
+ children: ReactNode | ((genAIStore: EnhancedStore) => ReactNode);
56
+ /**
57
+ * When `true`, the assistant operates against the caller's preview agent
58
+ * for this workspace (backend agent id: `{userId}-{workspaceId}-preview`).
59
+ * New conversations are created as preview conversations and the
60
+ * conversation list is filtered to preview conversations only.
61
+ *
62
+ * The preview agent must already exist and be enabled; otherwise
63
+ * conversation creation will fail.
64
+ *
65
+ * Note: toggling this prop rebuilds the chat state from scratch.
66
+ *
67
+ * @internal
68
+ */
69
+ isPreview?: boolean;
70
+ };
71
+ /**
72
+ * Provides the Gen AI store to the application.
73
+ * @public
74
+ */
75
+ export declare function GenAiStore(props: GenAiStoreProps): import("react/jsx-runtime").JSX.Element;